From 7e6600a8d74c8ce5402d616e12361540ac4e7159 Mon Sep 17 00:00:00 2001 From: Tristan d'Audibert Date: Mon, 16 Dec 2024 18:04:04 +0100 Subject: [PATCH 01/15] Add `config_btf_arg_depth` to extend `struct event_config` context. This commit introduces the `struct config_btf_arg_depth`. It appends `btf_argN` to `struct event_config` as an array. This array stores the path to the searched data. Any `btf_argN` can have a list of elements as follow : file->f_path is 152 bytes, so the array will look like [{ offset: 152, is_pointer: 0, is_initialized: 1 }, ...]. The max value `MAX_BTF_ARG_DEPTH` as been set arbitrary as the verifier need a fixed size. In config_btf_arg, is_pointer and is_initialized are u16 because it must match padding of 64 bits long structure Signed-off-by: Tristan d'Audibert --- bpf/process/types/basic.h | 18 ++++++++++++++++++ pkg/api/tracingapi/client_kprobe.go | 28 ++++++++++++++++++---------- 2 files changed, 36 insertions(+), 10 deletions(-) diff --git a/bpf/process/types/basic.h b/bpf/process/types/basic.h index 13a280b1ed4..131af095bfe 100644 --- a/bpf/process/types/basic.h +++ b/bpf/process/types/basic.h @@ -142,6 +142,19 @@ struct selector_arg_filters { __u32 argoff[5]; } __attribute__((packed)); +struct config_btf_arg { + __u32 offset; + __u16 is_pointer; + __u16 is_initialized; +} __attribute__((packed)); + +struct extract_arg_data { + struct config_btf_arg *btf_config; + unsigned long *arg; +}; + +#define MAX_BTF_ARG_DEPTH 10 + struct event_config { __u32 func_id; __s32 arg0; @@ -172,6 +185,11 @@ struct event_config { */ __u32 policy_id; __u32 flags; + struct config_btf_arg btf_arg0[MAX_BTF_ARG_DEPTH]; + struct config_btf_arg btf_arg1[MAX_BTF_ARG_DEPTH]; + struct config_btf_arg btf_arg2[MAX_BTF_ARG_DEPTH]; + struct config_btf_arg btf_arg3[MAX_BTF_ARG_DEPTH]; + struct config_btf_arg btf_arg4[MAX_BTF_ARG_DEPTH]; } __attribute__((packed)); #define MAX_ARGS_SIZE 80 diff --git a/pkg/api/tracingapi/client_kprobe.go b/pkg/api/tracingapi/client_kprobe.go index 67d0f15a154..aa3468f3bf7 100644 --- a/pkg/api/tracingapi/client_kprobe.go +++ b/pkg/api/tracingapi/client_kprobe.go @@ -574,17 +574,25 @@ type KprobeArgs struct { Args4 []byte } +type ConfigBtfArg struct { + Offset uint32 `align:"offset"` + IsPointer uint16 `align:"is_pointer"` + IsInitialized uint16 `align:"is_initialized"` +} + const EventConfigMaxArgs = 5 +const MaxBtfArgDepth = 10 // Artificial value for compilation, may be extended type EventConfig struct { - FuncId uint32 `align:"func_id"` - Arg [EventConfigMaxArgs]int32 `align:"arg0"` - ArgM [EventConfigMaxArgs]uint32 `align:"arg0m"` - ArgTpCtxOff [EventConfigMaxArgs]uint32 `align:"t_arg0_ctx_off"` - Syscall uint32 `align:"syscall"` - ArgReturnCopy int32 `align:"argreturncopy"` - ArgReturn int32 `align:"argreturn"` - ArgReturnAction int32 `align:"argreturnaction"` - PolicyID uint32 `align:"policy_id"` - Flags uint32 `align:"flags"` + FuncId uint32 `align:"func_id"` + Arg [EventConfigMaxArgs]int32 `align:"arg0"` + ArgM [EventConfigMaxArgs]uint32 `align:"arg0m"` + ArgTpCtxOff [EventConfigMaxArgs]uint32 `align:"t_arg0_ctx_off"` + Syscall uint32 `align:"syscall"` + ArgReturnCopy int32 `align:"argreturncopy"` + ArgReturn int32 `align:"argreturn"` + ArgReturnAction int32 `align:"argreturnaction"` + PolicyID uint32 `align:"policy_id"` + Flags uint32 `align:"flags"` + BtfArg [EventConfigMaxArgs][MaxBtfArgDepth]ConfigBtfArg `align:"btf_arg0"` } From 291d411ab9934da5f555c46635c0ea87e25fe21d Mon Sep 17 00:00:00 2001 From: Tristan d'Audibert Date: Fri, 10 Jan 2025 17:54:27 +0100 Subject: [PATCH 02/15] [generic_calls.h] Add loop to move on arg buffer based on config->btf_argN MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This commit introduces the “extract_arg_depth” function and loops over it to move into the “arg” buffer of config->btf_argN[i]->offset by iteration. The goal is to reach the requiered data by overwriting over arg with the new value. Signed-off-by: Tristan d'Audibert --- bpf/process/generic_calls.h | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/bpf/process/generic_calls.h b/bpf/process/generic_calls.h index 5f0c5042abd..9e727506cb2 100644 --- a/bpf/process/generic_calls.h +++ b/bpf/process/generic_calls.h @@ -62,11 +62,23 @@ generic_start_process_filter(void *ctx, struct bpf_map_def *calls) return 0; } +FUNC_INLINE int +extract_arg_depth(u32 i, struct extract_arg_data *data) +{ + if (i >= MAX_BTF_ARG_DEPTH || !data->btf_config[i].is_initialized) + return 1; + *data->arg = *data->arg + data->btf_config[i].offset; + if (data->btf_config[i].is_pointer) + probe_read((void *)data->arg, sizeof(char *), (void *)*data->arg); + return 0; +} + FUNC_INLINE int generic_process_event(void *ctx, struct bpf_map_def *tailcals) { struct msg_generic_kprobe *e; struct event_config *config; + struct config_btf_arg *btf_config; int index, zero = 0; unsigned long a; long ty, total; @@ -87,6 +99,23 @@ generic_process_event(void *ctx, struct bpf_map_def *tailcals) a = (&e->a0)[index]; total = e->common.size; + btf_config = (&config->btf_arg0)[index]; + if (index <= 4 && btf_config[0].is_initialized) { + struct extract_arg_data extract_data = { + .btf_config = btf_config, + .arg = &a, + }; +#ifndef __V61_BPF_PROG +#pragma unroll + for (int i = 0; i < MAX_BTF_ARG_DEPTH; ++i) { + if (extract_arg_depth(i, &extract_data)) + break; + } +#else + loop(MAX_BTF_ARG_DEPTH, extract_arg_depth, &extract_data, 0); +#endif /* __V61_BPF_PROG */ + } + /* Read out args1-5 */ ty = (&config->arg0)[index]; if (total < MAX_TOTAL) { From 083e578b5f40ff0eb6dde98ac12d88f630d4941e Mon Sep 17 00:00:00 2001 From: Tristan d'Audibert Date: Mon, 16 Dec 2024 17:29:46 +0100 Subject: [PATCH 03/15] Add `GenericTypeFromBTF` to search if supported type exists in btf.Type. This commit checks if btf.Type exists in Tetragon existing types. For instance: `struct file` with btf is called `file` and also exists in `GenericStringToType` with the same alias. However, the attribute `name` in `struct qstr` has a returned type `unsigned char` wich does not exist yet in `GenericStringToType`. The same thing happened with `linux_binprm->filename` as the type is `char` Signed-off-by: Tristan d'Audibert --- pkg/generictypes/generictypes.go | 24 +++++++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) diff --git a/pkg/generictypes/generictypes.go b/pkg/generictypes/generictypes.go index 3c490cefa08..c25a63ee39f 100644 --- a/pkg/generictypes/generictypes.go +++ b/pkg/generictypes/generictypes.go @@ -3,7 +3,11 @@ package generictypes -import "fmt" +import ( + "fmt" + + "github.com/cilium/ebpf/btf" +) const ( GenericIntType = 1 @@ -187,6 +191,24 @@ func GenericUserToKernelType(arg int) int { return ty } +func GenericTypeFromBTF(arg btf.Type) int { + ty, ok := GenericStringToType[arg.TypeName()] + if !ok { + switch t := arg.(type) { + case *btf.Restrict: + case *btf.Volatile: + case *btf.Const: + case *btf.Typedef: + return GenericTypeFromBTF(t.Type) + case *btf.Pointer: + return GenericTypeFromBTF(t.Target) + default: + return GenericInvalidType + } + } + return ty +} + func GenericTypeFromString(arg string) int { ty, ok := GenericStringToType[arg] if !ok { From a4713ecd373d80d042ada89deb431d477c1fdeef Mon Sep 17 00:00:00 2001 From: Tristan d'Audibert Date: Mon, 16 Dec 2024 17:46:21 +0100 Subject: [PATCH 04/15] Add `FindNextBtfType` function in btf.go to search for members in path FindNextBtfType function recursively searches in a btf structure in order to find a specific path until it reaches the target or fails. The function also searches in embedded anonymous structures or unions to cover as much use cases as possible. For instance, mm_struct has 2 fields; anonymous struct and another type. But you are still able to look into the anonymous struct by specifying a path like "mm.pgd.pgd". For instance, if the search is in the linux_binprm structure and the path is `file.f_path.dentry.d_name.name`, the following actions will be done. - Look for the variable name `file` inside `linux_binprm`. - If it matches, it stores the offset from linux_binprm where the `file` variable could be found. - Then it takes the btf type `file` and searches for a parameter named `f_path`. Signed-off-by: Tristan d'Audibert --- pkg/btf/btf.go | 93 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 93 insertions(+) diff --git a/pkg/btf/btf.go b/pkg/btf/btf.go index 068127b2c6d..1092cd1f503 100644 --- a/pkg/btf/btf.go +++ b/pkg/btf/btf.go @@ -7,8 +7,10 @@ import ( "fmt" "os" "path" + "reflect" "github.com/cilium/ebpf/btf" + api "github.com/cilium/tetragon/pkg/api/tracingapi" "github.com/cilium/tetragon/pkg/defaults" "github.com/cilium/tetragon/pkg/logger" "github.com/cilium/tetragon/pkg/option" @@ -101,3 +103,94 @@ func InitCachedBTF(lib, btf string) error { func GetCachedBTFFile() string { return btfFile } + +/* +FindNextBtfType function recursively search in a btf structure in order to +found a specific path until it reach the target or fail. + +The function also search in embedded anonymous structures or unions to cover as +much use cases as possible. For instance, mm_struct have 2 fields, anonymous +struct and another type. But you are still able to look into the anonymous +struct by specifying a path like "mm.pgd.pgd". + +@btfArgs: dest array for storing btf informations to reach the target on the +bpf side. +@currentType: The current type being proccessed, starts with root type. +@pathToFound: The string representation of the path to reach in the structures. +@i: The current depth, until last element of pathToFound. + +Return: The last type found matching the path, or error. +*/ +func FindNextBtfType( + btfArgs *[api.MaxBtfArgDepth]api.ConfigBtfArg, + currentType btf.Type, + pathToFound []string, + i int, +) (*btf.Type, error) { + switch t := currentType.(type) { + case *btf.Struct: + return processMembers(btfArgs, currentType, t.Members, pathToFound, i) + case *btf.Union: + return processMembers(btfArgs, currentType, t.Members, pathToFound, i) + case *btf.Pointer: + (*btfArgs)[i-1].IsPointer = uint16(1) + return FindNextBtfType(btfArgs, t.Target, pathToFound, i) + case *btf.Typedef: + return FindNextBtfType(btfArgs, t.Type, pathToFound, i) + default: + ty := currentType.TypeName() + if len(ty) == 0 { + ty = reflect.TypeOf(currentType).String() + } + return nil, fmt.Errorf("Unexpected type : %s has type %s", pathToFound[i-1], ty) + } +} + +func processMembers( + btfArgs *[api.MaxBtfArgDepth]api.ConfigBtfArg, + currentType btf.Type, + members []btf.Member, + pathToFound []string, + i int, +) (*btf.Type, error) { + var lastError error + memberWasFound := false + for _, member := range members { + if len(member.Name) == 0 { // If anonymous struct, fallthrough + (*btfArgs)[i].Offset = member.Offset.Bytes() + (*btfArgs)[i].IsInitialized = uint16(1) + lastTy, err := FindNextBtfType(btfArgs, member.Type, pathToFound, i) + if err != nil { + lastError = err + continue + } + return lastTy, nil + } + if member.Name == pathToFound[i] { + memberWasFound = true + (*btfArgs)[i].Offset = member.Offset.Bytes() + (*btfArgs)[i].IsInitialized = uint16(1) + isNotLastChild := i < len(pathToFound)-1 && i < api.MaxBtfArgDepth + if isNotLastChild { + return FindNextBtfType(btfArgs, member.Type, pathToFound, i+1) + } + currentType = member.Type + } + } + if !memberWasFound { + if lastError != nil { + return nil, lastError + } + return nil, fmt.Errorf( + "Attribute '%s' not found in structure '%s' found %v", + pathToFound[i], + currentType.TypeName(), + members, + ) + } + if t, ok := currentType.(*btf.Pointer); ok { + (*btfArgs)[i].IsPointer = uint16(1) + currentType = t.Target + } + return ¤tType, nil +} From b66a26d2b650d1764542eefc29900ddc89bb03ad Mon Sep 17 00:00:00 2001 From: Tristan d'Audibert Date: Tue, 31 Dec 2024 10:48:32 +0100 Subject: [PATCH 05/15] In `FindNextBtfType`, btf.Int types must be declared has pointer In order to read the data properly on BPF side, integer/long values must use `bpf_probe_read`. So now, every time the latest type retrieved is defined as an integer, it will be safely read before accessing the data. Signed-off-by: Tristan d'Audibert --- pkg/btf/btf.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/pkg/btf/btf.go b/pkg/btf/btf.go index 1092cd1f503..0e3a1a6f09c 100644 --- a/pkg/btf/btf.go +++ b/pkg/btf/btf.go @@ -191,6 +191,8 @@ func processMembers( if t, ok := currentType.(*btf.Pointer); ok { (*btfArgs)[i].IsPointer = uint16(1) currentType = t.Target + } else if _, ok := currentType.(*btf.Int); ok { + (*btfArgs)[i].IsPointer = uint16(1) } return ¤tType, nil } From 4c447f3578a3817e9c51ae75f03f8f7e2b0d8280 Mon Sep 17 00:00:00 2001 From: Tristan d'Audibert Date: Tue, 17 Dec 2024 08:50:45 +0100 Subject: [PATCH 06/15] Add `OverwriteType` and `ExtractParam` properties in `KProbeArg` This commit adds 2 parameters to give the ability to the user to search for a specific variable following a "path" as follow: ```yml ... args: - index: 0 type: "linux_binprm" extractParam: "file.f_path.dentry.d_name.name" overwriteType: "string" ... ``` The above config can be used to extract a specific parameter from the structure at index 0. Signed-off-by: Tristan d'Audibert --- .../crds-yaml/cilium.io_tracingpolicies.yaml | 45 +++++++++++++++++++ .../cilium.io_tracingpoliciesnamespaced.yaml | 45 +++++++++++++++++++ .../v1alpha1/cilium.io_tracingpolicies.yaml | 45 +++++++++++++++++++ .../cilium.io_tracingpoliciesnamespaced.yaml | 45 +++++++++++++++++++ pkg/k8s/apis/cilium.io/v1alpha1/types.go | 8 ++++ pkg/k8s/apis/cilium.io/v1alpha1/version.go | 2 +- .../v1alpha1/cilium.io_tracingpolicies.yaml | 45 +++++++++++++++++++ .../cilium.io_tracingpoliciesnamespaced.yaml | 45 +++++++++++++++++++ .../pkg/k8s/apis/cilium.io/v1alpha1/types.go | 8 ++++ .../k8s/apis/cilium.io/v1alpha1/version.go | 2 +- 10 files changed, 288 insertions(+), 2 deletions(-) diff --git a/install/kubernetes/tetragon/crds-yaml/cilium.io_tracingpolicies.yaml b/install/kubernetes/tetragon/crds-yaml/cilium.io_tracingpolicies.yaml index 641d9379157..da86e17d290 100644 --- a/install/kubernetes/tetragon/crds-yaml/cilium.io_tracingpolicies.yaml +++ b/install/kubernetes/tetragon/crds-yaml/cilium.io_tracingpolicies.yaml @@ -123,6 +123,10 @@ spec: trace output. items: properties: + extractParam: + default: "" + description: Extract Specific subargument in structure + type: string index: description: Position of the argument. format: int32 @@ -139,6 +143,11 @@ spec: will fetch at most 4096 bytes. In later kernels (>=5.4) tetragon supports fetching up to 327360 bytes if this flag is turned on type: boolean + overwriteType: + default: "" + description: Overwrite the Type argument. Use it only + with extraParam + type: string returnCopy: default: false description: |- @@ -221,6 +230,10 @@ spec: returnArg: description: A return argument to include in the trace output. properties: + extractParam: + default: "" + description: Extract Specific subargument in structure + type: string index: description: Position of the argument. format: int32 @@ -237,6 +250,11 @@ spec: will fetch at most 4096 bytes. In later kernels (>=5.4) tetragon supports fetching up to 327360 bytes if this flag is turned on type: boolean + overwriteType: + default: "" + description: Overwrite the Type argument. Use it only with + extraParam + type: string returnCopy: default: false description: |- @@ -834,6 +852,10 @@ spec: trace output. items: properties: + extractParam: + default: "" + description: Extract Specific subargument in structure + type: string index: description: Position of the argument. format: int32 @@ -850,6 +872,11 @@ spec: will fetch at most 4096 bytes. In later kernels (>=5.4) tetragon supports fetching up to 327360 bytes if this flag is turned on type: boolean + overwriteType: + default: "" + description: Overwrite the Type argument. Use it only + with extraParam + type: string returnCopy: default: false description: |- @@ -1484,6 +1511,10 @@ spec: trace output. items: properties: + extractParam: + default: "" + description: Extract Specific subargument in structure + type: string index: description: Position of the argument. format: int32 @@ -1500,6 +1531,11 @@ spec: will fetch at most 4096 bytes. In later kernels (>=5.4) tetragon supports fetching up to 327360 bytes if this flag is turned on type: boolean + overwriteType: + default: "" + description: Overwrite the Type argument. Use it only + with extraParam + type: string returnCopy: default: false description: |- @@ -2069,6 +2105,10 @@ spec: trace output. items: properties: + extractParam: + default: "" + description: Extract Specific subargument in structure + type: string index: description: Position of the argument. format: int32 @@ -2085,6 +2125,11 @@ spec: will fetch at most 4096 bytes. In later kernels (>=5.4) tetragon supports fetching up to 327360 bytes if this flag is turned on type: boolean + overwriteType: + default: "" + description: Overwrite the Type argument. Use it only + with extraParam + type: string returnCopy: default: false description: |- diff --git a/install/kubernetes/tetragon/crds-yaml/cilium.io_tracingpoliciesnamespaced.yaml b/install/kubernetes/tetragon/crds-yaml/cilium.io_tracingpoliciesnamespaced.yaml index a4cd73714ab..b3f8589a43e 100644 --- a/install/kubernetes/tetragon/crds-yaml/cilium.io_tracingpoliciesnamespaced.yaml +++ b/install/kubernetes/tetragon/crds-yaml/cilium.io_tracingpoliciesnamespaced.yaml @@ -123,6 +123,10 @@ spec: trace output. items: properties: + extractParam: + default: "" + description: Extract Specific subargument in structure + type: string index: description: Position of the argument. format: int32 @@ -139,6 +143,11 @@ spec: will fetch at most 4096 bytes. In later kernels (>=5.4) tetragon supports fetching up to 327360 bytes if this flag is turned on type: boolean + overwriteType: + default: "" + description: Overwrite the Type argument. Use it only + with extraParam + type: string returnCopy: default: false description: |- @@ -221,6 +230,10 @@ spec: returnArg: description: A return argument to include in the trace output. properties: + extractParam: + default: "" + description: Extract Specific subargument in structure + type: string index: description: Position of the argument. format: int32 @@ -237,6 +250,11 @@ spec: will fetch at most 4096 bytes. In later kernels (>=5.4) tetragon supports fetching up to 327360 bytes if this flag is turned on type: boolean + overwriteType: + default: "" + description: Overwrite the Type argument. Use it only with + extraParam + type: string returnCopy: default: false description: |- @@ -834,6 +852,10 @@ spec: trace output. items: properties: + extractParam: + default: "" + description: Extract Specific subargument in structure + type: string index: description: Position of the argument. format: int32 @@ -850,6 +872,11 @@ spec: will fetch at most 4096 bytes. In later kernels (>=5.4) tetragon supports fetching up to 327360 bytes if this flag is turned on type: boolean + overwriteType: + default: "" + description: Overwrite the Type argument. Use it only + with extraParam + type: string returnCopy: default: false description: |- @@ -1484,6 +1511,10 @@ spec: trace output. items: properties: + extractParam: + default: "" + description: Extract Specific subargument in structure + type: string index: description: Position of the argument. format: int32 @@ -1500,6 +1531,11 @@ spec: will fetch at most 4096 bytes. In later kernels (>=5.4) tetragon supports fetching up to 327360 bytes if this flag is turned on type: boolean + overwriteType: + default: "" + description: Overwrite the Type argument. Use it only + with extraParam + type: string returnCopy: default: false description: |- @@ -2069,6 +2105,10 @@ spec: trace output. items: properties: + extractParam: + default: "" + description: Extract Specific subargument in structure + type: string index: description: Position of the argument. format: int32 @@ -2085,6 +2125,11 @@ spec: will fetch at most 4096 bytes. In later kernels (>=5.4) tetragon supports fetching up to 327360 bytes if this flag is turned on type: boolean + overwriteType: + default: "" + description: Overwrite the Type argument. Use it only + with extraParam + type: string returnCopy: default: false description: |- diff --git a/pkg/k8s/apis/cilium.io/client/crds/v1alpha1/cilium.io_tracingpolicies.yaml b/pkg/k8s/apis/cilium.io/client/crds/v1alpha1/cilium.io_tracingpolicies.yaml index 641d9379157..da86e17d290 100644 --- a/pkg/k8s/apis/cilium.io/client/crds/v1alpha1/cilium.io_tracingpolicies.yaml +++ b/pkg/k8s/apis/cilium.io/client/crds/v1alpha1/cilium.io_tracingpolicies.yaml @@ -123,6 +123,10 @@ spec: trace output. items: properties: + extractParam: + default: "" + description: Extract Specific subargument in structure + type: string index: description: Position of the argument. format: int32 @@ -139,6 +143,11 @@ spec: will fetch at most 4096 bytes. In later kernels (>=5.4) tetragon supports fetching up to 327360 bytes if this flag is turned on type: boolean + overwriteType: + default: "" + description: Overwrite the Type argument. Use it only + with extraParam + type: string returnCopy: default: false description: |- @@ -221,6 +230,10 @@ spec: returnArg: description: A return argument to include in the trace output. properties: + extractParam: + default: "" + description: Extract Specific subargument in structure + type: string index: description: Position of the argument. format: int32 @@ -237,6 +250,11 @@ spec: will fetch at most 4096 bytes. In later kernels (>=5.4) tetragon supports fetching up to 327360 bytes if this flag is turned on type: boolean + overwriteType: + default: "" + description: Overwrite the Type argument. Use it only with + extraParam + type: string returnCopy: default: false description: |- @@ -834,6 +852,10 @@ spec: trace output. items: properties: + extractParam: + default: "" + description: Extract Specific subargument in structure + type: string index: description: Position of the argument. format: int32 @@ -850,6 +872,11 @@ spec: will fetch at most 4096 bytes. In later kernels (>=5.4) tetragon supports fetching up to 327360 bytes if this flag is turned on type: boolean + overwriteType: + default: "" + description: Overwrite the Type argument. Use it only + with extraParam + type: string returnCopy: default: false description: |- @@ -1484,6 +1511,10 @@ spec: trace output. items: properties: + extractParam: + default: "" + description: Extract Specific subargument in structure + type: string index: description: Position of the argument. format: int32 @@ -1500,6 +1531,11 @@ spec: will fetch at most 4096 bytes. In later kernels (>=5.4) tetragon supports fetching up to 327360 bytes if this flag is turned on type: boolean + overwriteType: + default: "" + description: Overwrite the Type argument. Use it only + with extraParam + type: string returnCopy: default: false description: |- @@ -2069,6 +2105,10 @@ spec: trace output. items: properties: + extractParam: + default: "" + description: Extract Specific subargument in structure + type: string index: description: Position of the argument. format: int32 @@ -2085,6 +2125,11 @@ spec: will fetch at most 4096 bytes. In later kernels (>=5.4) tetragon supports fetching up to 327360 bytes if this flag is turned on type: boolean + overwriteType: + default: "" + description: Overwrite the Type argument. Use it only + with extraParam + type: string returnCopy: default: false description: |- diff --git a/pkg/k8s/apis/cilium.io/client/crds/v1alpha1/cilium.io_tracingpoliciesnamespaced.yaml b/pkg/k8s/apis/cilium.io/client/crds/v1alpha1/cilium.io_tracingpoliciesnamespaced.yaml index a4cd73714ab..b3f8589a43e 100644 --- a/pkg/k8s/apis/cilium.io/client/crds/v1alpha1/cilium.io_tracingpoliciesnamespaced.yaml +++ b/pkg/k8s/apis/cilium.io/client/crds/v1alpha1/cilium.io_tracingpoliciesnamespaced.yaml @@ -123,6 +123,10 @@ spec: trace output. items: properties: + extractParam: + default: "" + description: Extract Specific subargument in structure + type: string index: description: Position of the argument. format: int32 @@ -139,6 +143,11 @@ spec: will fetch at most 4096 bytes. In later kernels (>=5.4) tetragon supports fetching up to 327360 bytes if this flag is turned on type: boolean + overwriteType: + default: "" + description: Overwrite the Type argument. Use it only + with extraParam + type: string returnCopy: default: false description: |- @@ -221,6 +230,10 @@ spec: returnArg: description: A return argument to include in the trace output. properties: + extractParam: + default: "" + description: Extract Specific subargument in structure + type: string index: description: Position of the argument. format: int32 @@ -237,6 +250,11 @@ spec: will fetch at most 4096 bytes. In later kernels (>=5.4) tetragon supports fetching up to 327360 bytes if this flag is turned on type: boolean + overwriteType: + default: "" + description: Overwrite the Type argument. Use it only with + extraParam + type: string returnCopy: default: false description: |- @@ -834,6 +852,10 @@ spec: trace output. items: properties: + extractParam: + default: "" + description: Extract Specific subargument in structure + type: string index: description: Position of the argument. format: int32 @@ -850,6 +872,11 @@ spec: will fetch at most 4096 bytes. In later kernels (>=5.4) tetragon supports fetching up to 327360 bytes if this flag is turned on type: boolean + overwriteType: + default: "" + description: Overwrite the Type argument. Use it only + with extraParam + type: string returnCopy: default: false description: |- @@ -1484,6 +1511,10 @@ spec: trace output. items: properties: + extractParam: + default: "" + description: Extract Specific subargument in structure + type: string index: description: Position of the argument. format: int32 @@ -1500,6 +1531,11 @@ spec: will fetch at most 4096 bytes. In later kernels (>=5.4) tetragon supports fetching up to 327360 bytes if this flag is turned on type: boolean + overwriteType: + default: "" + description: Overwrite the Type argument. Use it only + with extraParam + type: string returnCopy: default: false description: |- @@ -2069,6 +2105,10 @@ spec: trace output. items: properties: + extractParam: + default: "" + description: Extract Specific subargument in structure + type: string index: description: Position of the argument. format: int32 @@ -2085,6 +2125,11 @@ spec: will fetch at most 4096 bytes. In later kernels (>=5.4) tetragon supports fetching up to 327360 bytes if this flag is turned on type: boolean + overwriteType: + default: "" + description: Overwrite the Type argument. Use it only + with extraParam + type: string returnCopy: default: false description: |- diff --git a/pkg/k8s/apis/cilium.io/v1alpha1/types.go b/pkg/k8s/apis/cilium.io/v1alpha1/types.go index e46bb752c41..9a058f98bd9 100644 --- a/pkg/k8s/apis/cilium.io/v1alpha1/types.go +++ b/pkg/k8s/apis/cilium.io/v1alpha1/types.go @@ -65,6 +65,14 @@ type KProbeArg struct { // Argument type. Type string `json:"type"` // +kubebuilder:validation:Optional + // +kubebuilder:default="" + // Extract Specific subargument in structure + ExtractParam string `json:"extractParam"` + // +kubebuilder:validation:Optional + // +kubebuilder:default="" + // Overwrite the Type argument. Use it only with extraParam + OverwriteType string `json:"overwriteType"` + // +kubebuilder:validation:Optional // +kubebuilder:validation:Minimum=0 // Specifies the position of the corresponding size argument for this argument. // This field is used only for char_buf and char_iovec types. diff --git a/pkg/k8s/apis/cilium.io/v1alpha1/version.go b/pkg/k8s/apis/cilium.io/v1alpha1/version.go index 477be53db5b..eca763fceff 100644 --- a/pkg/k8s/apis/cilium.io/v1alpha1/version.go +++ b/pkg/k8s/apis/cilium.io/v1alpha1/version.go @@ -7,4 +7,4 @@ package v1alpha1 // Used to determine if CRD needs to be updated in cluster // // Developers: Bump patch for each change in the CRD schema. -const CustomResourceDefinitionSchemaVersion = "1.4.0" +const CustomResourceDefinitionSchemaVersion = "1.4.1" diff --git a/vendor/github.com/cilium/tetragon/pkg/k8s/apis/cilium.io/client/crds/v1alpha1/cilium.io_tracingpolicies.yaml b/vendor/github.com/cilium/tetragon/pkg/k8s/apis/cilium.io/client/crds/v1alpha1/cilium.io_tracingpolicies.yaml index 641d9379157..da86e17d290 100644 --- a/vendor/github.com/cilium/tetragon/pkg/k8s/apis/cilium.io/client/crds/v1alpha1/cilium.io_tracingpolicies.yaml +++ b/vendor/github.com/cilium/tetragon/pkg/k8s/apis/cilium.io/client/crds/v1alpha1/cilium.io_tracingpolicies.yaml @@ -123,6 +123,10 @@ spec: trace output. items: properties: + extractParam: + default: "" + description: Extract Specific subargument in structure + type: string index: description: Position of the argument. format: int32 @@ -139,6 +143,11 @@ spec: will fetch at most 4096 bytes. In later kernels (>=5.4) tetragon supports fetching up to 327360 bytes if this flag is turned on type: boolean + overwriteType: + default: "" + description: Overwrite the Type argument. Use it only + with extraParam + type: string returnCopy: default: false description: |- @@ -221,6 +230,10 @@ spec: returnArg: description: A return argument to include in the trace output. properties: + extractParam: + default: "" + description: Extract Specific subargument in structure + type: string index: description: Position of the argument. format: int32 @@ -237,6 +250,11 @@ spec: will fetch at most 4096 bytes. In later kernels (>=5.4) tetragon supports fetching up to 327360 bytes if this flag is turned on type: boolean + overwriteType: + default: "" + description: Overwrite the Type argument. Use it only with + extraParam + type: string returnCopy: default: false description: |- @@ -834,6 +852,10 @@ spec: trace output. items: properties: + extractParam: + default: "" + description: Extract Specific subargument in structure + type: string index: description: Position of the argument. format: int32 @@ -850,6 +872,11 @@ spec: will fetch at most 4096 bytes. In later kernels (>=5.4) tetragon supports fetching up to 327360 bytes if this flag is turned on type: boolean + overwriteType: + default: "" + description: Overwrite the Type argument. Use it only + with extraParam + type: string returnCopy: default: false description: |- @@ -1484,6 +1511,10 @@ spec: trace output. items: properties: + extractParam: + default: "" + description: Extract Specific subargument in structure + type: string index: description: Position of the argument. format: int32 @@ -1500,6 +1531,11 @@ spec: will fetch at most 4096 bytes. In later kernels (>=5.4) tetragon supports fetching up to 327360 bytes if this flag is turned on type: boolean + overwriteType: + default: "" + description: Overwrite the Type argument. Use it only + with extraParam + type: string returnCopy: default: false description: |- @@ -2069,6 +2105,10 @@ spec: trace output. items: properties: + extractParam: + default: "" + description: Extract Specific subargument in structure + type: string index: description: Position of the argument. format: int32 @@ -2085,6 +2125,11 @@ spec: will fetch at most 4096 bytes. In later kernels (>=5.4) tetragon supports fetching up to 327360 bytes if this flag is turned on type: boolean + overwriteType: + default: "" + description: Overwrite the Type argument. Use it only + with extraParam + type: string returnCopy: default: false description: |- diff --git a/vendor/github.com/cilium/tetragon/pkg/k8s/apis/cilium.io/client/crds/v1alpha1/cilium.io_tracingpoliciesnamespaced.yaml b/vendor/github.com/cilium/tetragon/pkg/k8s/apis/cilium.io/client/crds/v1alpha1/cilium.io_tracingpoliciesnamespaced.yaml index a4cd73714ab..b3f8589a43e 100644 --- a/vendor/github.com/cilium/tetragon/pkg/k8s/apis/cilium.io/client/crds/v1alpha1/cilium.io_tracingpoliciesnamespaced.yaml +++ b/vendor/github.com/cilium/tetragon/pkg/k8s/apis/cilium.io/client/crds/v1alpha1/cilium.io_tracingpoliciesnamespaced.yaml @@ -123,6 +123,10 @@ spec: trace output. items: properties: + extractParam: + default: "" + description: Extract Specific subargument in structure + type: string index: description: Position of the argument. format: int32 @@ -139,6 +143,11 @@ spec: will fetch at most 4096 bytes. In later kernels (>=5.4) tetragon supports fetching up to 327360 bytes if this flag is turned on type: boolean + overwriteType: + default: "" + description: Overwrite the Type argument. Use it only + with extraParam + type: string returnCopy: default: false description: |- @@ -221,6 +230,10 @@ spec: returnArg: description: A return argument to include in the trace output. properties: + extractParam: + default: "" + description: Extract Specific subargument in structure + type: string index: description: Position of the argument. format: int32 @@ -237,6 +250,11 @@ spec: will fetch at most 4096 bytes. In later kernels (>=5.4) tetragon supports fetching up to 327360 bytes if this flag is turned on type: boolean + overwriteType: + default: "" + description: Overwrite the Type argument. Use it only with + extraParam + type: string returnCopy: default: false description: |- @@ -834,6 +852,10 @@ spec: trace output. items: properties: + extractParam: + default: "" + description: Extract Specific subargument in structure + type: string index: description: Position of the argument. format: int32 @@ -850,6 +872,11 @@ spec: will fetch at most 4096 bytes. In later kernels (>=5.4) tetragon supports fetching up to 327360 bytes if this flag is turned on type: boolean + overwriteType: + default: "" + description: Overwrite the Type argument. Use it only + with extraParam + type: string returnCopy: default: false description: |- @@ -1484,6 +1511,10 @@ spec: trace output. items: properties: + extractParam: + default: "" + description: Extract Specific subargument in structure + type: string index: description: Position of the argument. format: int32 @@ -1500,6 +1531,11 @@ spec: will fetch at most 4096 bytes. In later kernels (>=5.4) tetragon supports fetching up to 327360 bytes if this flag is turned on type: boolean + overwriteType: + default: "" + description: Overwrite the Type argument. Use it only + with extraParam + type: string returnCopy: default: false description: |- @@ -2069,6 +2105,10 @@ spec: trace output. items: properties: + extractParam: + default: "" + description: Extract Specific subargument in structure + type: string index: description: Position of the argument. format: int32 @@ -2085,6 +2125,11 @@ spec: will fetch at most 4096 bytes. In later kernels (>=5.4) tetragon supports fetching up to 327360 bytes if this flag is turned on type: boolean + overwriteType: + default: "" + description: Overwrite the Type argument. Use it only + with extraParam + type: string returnCopy: default: false description: |- diff --git a/vendor/github.com/cilium/tetragon/pkg/k8s/apis/cilium.io/v1alpha1/types.go b/vendor/github.com/cilium/tetragon/pkg/k8s/apis/cilium.io/v1alpha1/types.go index e46bb752c41..9a058f98bd9 100644 --- a/vendor/github.com/cilium/tetragon/pkg/k8s/apis/cilium.io/v1alpha1/types.go +++ b/vendor/github.com/cilium/tetragon/pkg/k8s/apis/cilium.io/v1alpha1/types.go @@ -65,6 +65,14 @@ type KProbeArg struct { // Argument type. Type string `json:"type"` // +kubebuilder:validation:Optional + // +kubebuilder:default="" + // Extract Specific subargument in structure + ExtractParam string `json:"extractParam"` + // +kubebuilder:validation:Optional + // +kubebuilder:default="" + // Overwrite the Type argument. Use it only with extraParam + OverwriteType string `json:"overwriteType"` + // +kubebuilder:validation:Optional // +kubebuilder:validation:Minimum=0 // Specifies the position of the corresponding size argument for this argument. // This field is used only for char_buf and char_iovec types. diff --git a/vendor/github.com/cilium/tetragon/pkg/k8s/apis/cilium.io/v1alpha1/version.go b/vendor/github.com/cilium/tetragon/pkg/k8s/apis/cilium.io/v1alpha1/version.go index 477be53db5b..eca763fceff 100644 --- a/vendor/github.com/cilium/tetragon/pkg/k8s/apis/cilium.io/v1alpha1/version.go +++ b/vendor/github.com/cilium/tetragon/pkg/k8s/apis/cilium.io/v1alpha1/version.go @@ -7,4 +7,4 @@ package v1alpha1 // Used to determine if CRD needs to be updated in cluster // // Developers: Bump patch for each change in the CRD schema. -const CustomResourceDefinitionSchemaVersion = "1.4.0" +const CustomResourceDefinitionSchemaVersion = "1.4.1" From 8c3b2c5993b5e32ce3b83b374aad67e5e7c23fee Mon Sep 17 00:00:00 2001 From: Tristan d'Audibert Date: Mon, 16 Dec 2024 17:49:26 +0100 Subject: [PATCH 07/15] kernel.go : update `argSelectorType` to use sig.OverwriteType if set. The OverwriteType parameter should be deleted if `argSelectorType` can use the `EventConfig` structure to search for the correct Type. Signed-off-by: Tristan d'Audibert --- pkg/selectors/kernel.go | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/pkg/selectors/kernel.go b/pkg/selectors/kernel.go index 7d4f02d8740..9316c7e1ec7 100644 --- a/pkg/selectors/kernel.go +++ b/pkg/selectors/kernel.go @@ -357,7 +357,11 @@ func argSelectorType(arg *v1alpha1.ArgSelector, sig []v1alpha1.KProbeArg) (uint3 if arg.Index == s.Index { // TBD: We shouldn't get this far with invalid KProbe args // KProbe args have already been validated - return uint32(gt.GenericTypeFromString(s.Type)), nil + ty := s.Type + if s.ExtractParam != "" && s.OverwriteType != "" { + ty = s.OverwriteType + } + return uint32(gt.GenericTypeFromString(ty)), nil } } return 0, fmt.Errorf("argFilter for unknown index") From 48549b38c5752fbe78391087e28119d95d02e31a Mon Sep 17 00:00:00 2001 From: Tristan d'Audibert Date: Mon, 16 Dec 2024 17:55:26 +0100 Subject: [PATCH 08/15] [generic.go] Take ExtractParam from KprobeArg to build ConfigBtfArg Searches if every user defined type with ExtractParam exists as a BTF type and stores its corresponding offset in ConfigBtfArg. This function does a basic split on `ExtractParam` to obtain the "path" to the required data. Then, the array is given to `btf.FindNextBTFType` to find the offset of each element until we reach the required data. The output is stored in EventConfig to keep the normal behaviour. For example, if the arg 0 is `struct linux_binprm` and ExtractParam is set to `file.f_path.dentry.d_name.name`, the output will give an array of all the offsets from their parents as follows : [{ offset: 96, is_pointer: 0 }, { offset: 152, is_pointer: 1 }, ...] Signed-off-by: Tristan d'Audibert --- pkg/sensors/tracing/generic.go | 46 ++++++++++++++++++++++++++++++++++ 1 file changed, 46 insertions(+) create mode 100644 pkg/sensors/tracing/generic.go diff --git a/pkg/sensors/tracing/generic.go b/pkg/sensors/tracing/generic.go new file mode 100644 index 00000000000..a89ba8b5cb3 --- /dev/null +++ b/pkg/sensors/tracing/generic.go @@ -0,0 +1,46 @@ +// SPDX-License-Identifier: Apache-2.0 +// Copyright Authors of Tetragon +// Copyright Orange + +package tracing + +import ( + "fmt" + "strings" + + _btf "github.com/cilium/ebpf/btf" + api "github.com/cilium/tetragon/pkg/api/tracingapi" + "github.com/cilium/tetragon/pkg/btf" + gt "github.com/cilium/tetragon/pkg/generictypes" + "github.com/cilium/tetragon/pkg/k8s/apis/cilium.io/v1alpha1" +) + +func buildBtfArg(arg v1alpha1.KProbeArg, btfArg *[api.MaxBtfArgDepth]api.ConfigBtfArg) (*_btf.Type, error) { + spec, err := btf.NewBTF() + if err != nil { + return nil, fmt.Errorf("Unable to load BTF file with error : %v", err) + } + + partialPath := strings.Split(arg.ExtractParam, ".") + if len(partialPath) > api.MaxBtfArgDepth { + return nil, fmt.Errorf("Exausted research in BTF for type %s. The maximum depth allowed is %d", arg.Type, api.MaxBtfArgDepth) + } + + rootType, err := spec.AnyTypeByName(arg.Type) + if err != nil { + return nil, fmt.Errorf("Type %s has not been found in BTF", arg.Type) + } + lastBtfType, err := btf.FindNextBtfType(btfArg, rootType, partialPath, 0) + if err != nil { + return nil, err + } + return lastBtfType, nil +} + +func findTypeFromBtfType(arg v1alpha1.KProbeArg, btfType *_btf.Type) int { + if arg.OverwriteType != "" { + return gt.GenericTypeFromString(arg.OverwriteType) + } else { + return gt.GenericTypeFromBTF(*btfType) + } +} From fa9ea58dfcfb729f1dc29d2602b49f8c5ea39a7a Mon Sep 17 00:00:00 2001 From: Tristan d'Audibert Date: Mon, 16 Dec 2024 17:55:56 +0100 Subject: [PATCH 09/15] [genericlsm.go] Use ExtractParam to build ConfigBtfArg for LSM This commit updates `addLsm` function to use the `ExtractParam` and `OverwriteType` in order to look for the attributes in BTF structure. Signed-off-by: Tristan d'Audibert --- pkg/sensors/tracing/genericlsm.go | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/pkg/sensors/tracing/genericlsm.go b/pkg/sensors/tracing/genericlsm.go index 699ba08b2ea..6ab469ec2dd 100644 --- a/pkg/sensors/tracing/genericlsm.go +++ b/pkg/sensors/tracing/genericlsm.go @@ -209,6 +209,7 @@ type addLsmIn struct { func addLsm(f *v1alpha1.LsmHookSpec, in *addLsmIn) (id idtable.EntryID, err error) { var argSigPrinters []argPrinter var argsBTFSet [api.MaxArgsSupported]bool + var allBtfArgs [api.EventConfigMaxArgs][api.MaxBtfArgDepth]api.ConfigBtfArg errFn := func(err error) (idtable.EntryID, error) { return idtable.UninitializedEntryID, err @@ -239,6 +240,16 @@ func addLsm(f *v1alpha1.LsmHookSpec, in *addLsmIn) (id idtable.EntryID, err erro if argType == gt.GenericInvalidType { return errFn(fmt.Errorf("Arg(%d) type '%s' unsupported", j, a.Type)) } + + if a.ExtractParam != "" && j < api.EventConfigMaxArgs { + allBtfArgs[j] = [api.MaxBtfArgDepth]api.ConfigBtfArg{} + lastBtfType, err := buildBtfArg(a, &allBtfArgs[j]) + if err != nil { + return errFn(err) + } + argType = findTypeFromBtfType(a, lastBtfType) + } + if a.MaxData { if argType != gt.GenericCharBuffer { logger.GetLogger().Warnf("maxData flag is ignored (supported for char_buf type)") @@ -263,6 +274,7 @@ func addLsm(f *v1alpha1.LsmHookSpec, in *addLsmIn) (id idtable.EntryID, err erro argSigPrinters = append(argSigPrinters, argP) } + config.BtfArg = allBtfArgs config.ArgReturn = int32(0) config.ArgReturnCopy = int32(0) From 9ec44b43a5af53480eea50d9a41fe94fd408da93 Mon Sep 17 00:00:00 2001 From: Tristan d'Audibert Date: Mon, 16 Dec 2024 17:56:15 +0100 Subject: [PATCH 10/15] [genericuprobe.go] Add exception for ExtractParam/OverwriteType for uprobes As BTF types are not defined for Uprobes, their offsets can't be found in BTF file. Thus, with this commit, if the user defines ExtractParam / OverwriteType, their are ignored and a warning is displayed Signed-off-by: Tristan d'Audibert --- pkg/sensors/tracing/genericuprobe.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/pkg/sensors/tracing/genericuprobe.go b/pkg/sensors/tracing/genericuprobe.go index 3406ff772e7..184f9fe2469 100644 --- a/pkg/sensors/tracing/genericuprobe.go +++ b/pkg/sensors/tracing/genericuprobe.go @@ -341,6 +341,10 @@ func addUprobe(spec *v1alpha1.UProbeSpec, ids []idtable.EntryID, in *addUprobeIn return nil, fmt.Errorf("Error add arg: ArgType %s Index %d out of bounds", a.Type, int(a.Index)) } + + if a.ExtractParam != "" || a.OverwriteType != "" { + logger.GetLogger().Warnf("Extracting parameters from Uprobes is not supported, ignoring") + } argTypes[a.Index] = int32(argType) argMeta[a.Index] = uint32(argMValue) argSet[a.Index] = true From 135ade19b6077e274fbfb07780fe262ae20db2b2 Mon Sep 17 00:00:00 2001 From: Tristan d'Audibert Date: Mon, 16 Dec 2024 17:56:26 +0100 Subject: [PATCH 11/15] [generickprobe.go] Add ExtractParam/OverwriteType usage for kprobes Add very similar code as in `genericlsm.go` file to handle ExtractParam feature. Signed-off-by: Tristan d'Audibert --- pkg/sensors/tracing/generickprobe.go | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/pkg/sensors/tracing/generickprobe.go b/pkg/sensors/tracing/generickprobe.go index 0b7a7f97222..d434ac64b2d 100644 --- a/pkg/sensors/tracing/generickprobe.go +++ b/pkg/sensors/tracing/generickprobe.go @@ -693,6 +693,7 @@ func addKprobe(funcName string, instance int, f *v1alpha1.KProbeSpec, in *addKpr var setRetprobe bool var argRetprobe *v1alpha1.KProbeArg var argsBTFSet [api.MaxArgsSupported]bool + var allBtfArgs [api.EventConfigMaxArgs][api.MaxBtfArgDepth]api.ConfigBtfArg errFn := func(err error) (idtable.EntryID, error) { return idtable.UninitializedEntryID, err @@ -759,6 +760,15 @@ func addKprobe(funcName string, instance int, f *v1alpha1.KProbeSpec, in *addKpr argType = gt.GenericTypeFromString(a.Type) } + if a.ExtractParam != "" && j < api.EventConfigMaxArgs { + allBtfArgs[j] = [api.MaxBtfArgDepth]api.ConfigBtfArg{} + lastBtfType, err := buildBtfArg(a, &allBtfArgs[j]) + if err != nil { + return errFn(err) + } + argType = findTypeFromBtfType(a, lastBtfType) + } + if argType == gt.GenericInvalidType { return errFn(fmt.Errorf("Arg(%d) type '%s' unsupported", j, a.Type)) } @@ -782,6 +792,7 @@ func addKprobe(funcName string, instance int, f *v1alpha1.KProbeSpec, in *addKpr return errFn(fmt.Errorf("Error add arg: ArgType %s Index %d out of bounds", a.Type, int(a.Index))) } + config.BtfArg = allBtfArgs config.Arg[a.Index] = int32(argType) config.ArgM[a.Index] = uint32(argMValue) From e80e73ded0bbe3502c0d14e55d640a28459c82c4 Mon Sep 17 00:00:00 2001 From: Tristan d'Audibert Date: Mon, 16 Dec 2024 17:57:07 +0100 Subject: [PATCH 12/15] [btf_test.go] Add unit tests for FindNextBtfType with multiple cases. This commit adds 3 tests for FindNextBtfType algorithm. The first is `testAssertEqualBtfPath` to assert that a specific path has the exact same btfConfig as expected. The second, "testAssertPathIsAccessible" tries to reach the path and asserts that no errors are raised. The third test "testAssertErrorOnInvalidPath" asserts that the error messages raised if the path is incorrect. The chosen test cases have embed union/anonymous structs. The important thing to notice in case of adding new tests is to be aware of btf changes on different architectures. Especially for `testAssertEqualBtfPath` where Offset could be different. To Test locally : ``` go test -exec "sudo" ./pkg/btf/ ``` Signed-off-by: Tristan d'Audibert --- pkg/btf/btf_test.go | 94 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 94 insertions(+) diff --git a/pkg/btf/btf_test.go b/pkg/btf/btf_test.go index ef99e135f98..1dd9729b339 100644 --- a/pkg/btf/btf_test.go +++ b/pkg/btf/btf_test.go @@ -7,8 +7,11 @@ import ( "fmt" "os" "path/filepath" + "strings" "testing" + btf "github.com/cilium/ebpf/btf" + api "github.com/cilium/tetragon/pkg/api/tracingapi" "github.com/cilium/tetragon/pkg/defaults" "github.com/stretchr/testify/assert" "golang.org/x/sys/unix" @@ -161,3 +164,94 @@ func TestInitCachedBTF(t *testing.T) { assert.EqualValues(t, defaults.DefaultBTFFile, btffile, "GetCachedBTFFile() - want:'%s' - got:'%s'", defaults.DefaultBTFFile, btffile) } } + +func testFindNextBtf(t *testing.T, spec *btf.Spec, rootTypeStr string, strPath string) (*[api.MaxBtfArgDepth]api.ConfigBtfArg, *btf.Type, error) { + var btfArgs [api.MaxBtfArgDepth]api.ConfigBtfArg + path := strings.Split(strPath, ".") + + rootType, err := spec.AnyTypeByName(rootTypeStr) + if err != nil { + assert.Error(t, err) + } + lastBtfType, err := FindNextBtfType(&btfArgs, rootType, path, 0) + if err != nil { + return nil, nil, err + } + + return &btfArgs, lastBtfType, nil +} + +func testAssertEqualPath(t *testing.T, spec *btf.Spec, rootTypeStr string, strPath string, resBtfArgs []api.ConfigBtfArg) { + var btfArgsToVerify [api.MaxBtfArgDepth]api.ConfigBtfArg + + for i, item := range resBtfArgs { + if i < api.MaxBtfArgDepth { + btfArgsToVerify[i] = item + } + } + + btfArgs, _, err := testFindNextBtf(t, spec, rootTypeStr, strPath) + + assert.NoError(t, err) + assert.Equal(t, *btfArgs, btfArgsToVerify) +} + +func testAssertEqualBtfPath(t *testing.T, spec *btf.Spec) { + // Test default behaviour + testAssertEqualPath( + t, + spec, + "linux_binprm", + "file.f_path.dentry.d_name.name", + []api.ConfigBtfArg{ + {Offset: 64, IsPointer: 1, IsInitialized: 1}, + {Offset: 152, IsPointer: 0, IsInitialized: 1}, + {Offset: 8, IsPointer: 1, IsInitialized: 1}, + {Offset: 32, IsPointer: 0, IsInitialized: 1}, + {Offset: 8, IsPointer: 1, IsInitialized: 1}, + }) + + // Test anonymous struct + testAssertEqualPath( + t, + spec, + "linux_binprm", + "mm.arg_start", + []api.ConfigBtfArg{ + {Offset: 16, IsPointer: 1, IsInitialized: 1}, + {Offset: 368, IsPointer: 1, IsInitialized: 1}, + }) + // Test Union + testAssertEqualPath( + t, + spec, + "linux_binprm", + "file.f_inode.i_dir_seq", + []api.ConfigBtfArg{ + {Offset: 64, IsPointer: 1, IsInitialized: 1}, + {Offset: 168, IsPointer: 1, IsInitialized: 1}, + {Offset: 0, IsPointer: 1, IsInitialized: 1}, + }) +} +func testAssertPathIsAccessible(t *testing.T, spec *btf.Spec) { + _, _, err := testFindNextBtf(t, spec, "task_struct", "trc_reader_special.b.need_mb") + assert.NoError(t, err) + + _, _, err = testFindNextBtf(t, spec, "linux_binprm", "mm.pgd.pgd") + assert.NoError(t, err) +} + +func testAssertErrorOnInvalidPath(t *testing.T, spec *btf.Spec) { + _, _, err := testFindNextBtf(t, spec, "linux_binprm", "mm.pgd.fail") + assert.ErrorContains(t, err, "Attribute 'fail' not found in structure ''") +} + +func TestFindNextBtf(t *testing.T) { + spec, err := NewBTF() + if err != nil { + assert.Error(t, err) + } + testAssertPathIsAccessible(t, spec) + testAssertErrorOnInvalidPath(t, spec) + testAssertEqualBtfPath(t, spec) +} From bd2b422f7f26a79efcb065b0812be795c02e78b4 Mon Sep 17 00:00:00 2001 From: Tristan d'Audibert Date: Mon, 16 Dec 2024 17:57:23 +0100 Subject: [PATCH 13/15] [generic_test.go] Add unit test for ExtractParam with fake LSM policy This test aims at testing the ExtractParam feature. Tetragon use hard-coded types instead of BTF. So currently, the ExtractParam feature does not allow to extract attributes from other types than the few hard-coded in `generictypes.go`. For instance, if you try to use the feature with task_struct structure, it will fail because it is not yet hard-coded. Signed-off-by: Tristan d'Audibert --- pkg/sensors/tracing/generic_test.go | 63 +++++++++++++++++++++++++++++ 1 file changed, 63 insertions(+) create mode 100644 pkg/sensors/tracing/generic_test.go diff --git a/pkg/sensors/tracing/generic_test.go b/pkg/sensors/tracing/generic_test.go new file mode 100644 index 00000000000..a0a4ce16a47 --- /dev/null +++ b/pkg/sensors/tracing/generic_test.go @@ -0,0 +1,63 @@ +// SPDX-License-Identifier: Apache-2.0 +// Copyright Authors of Tetragon +// Copyright Orange + +package tracing + +import ( + "testing" + + api "github.com/cilium/tetragon/pkg/api/tracingapi" + gt "github.com/cilium/tetragon/pkg/generictypes" + "github.com/cilium/tetragon/pkg/tracingpolicy" + "github.com/stretchr/testify/assert" +) + +func TestBuildBtfArgFromLSMPolicy(t *testing.T) { + rawPolicy := ` +apiVersion: cilium.io/v1alpha1 +kind: TracingPolicy +metadata: + name: "lsm" +spec: + lsmhooks: + - hook: "fake" + args: + - index: 0 + type: "linux_binprm" + extractParam: 'mm.arg_start' + overwriteType: 'int' + - index: 1 + type: "linux_binprm" + extractParam: 'file.f_path.dentry.d_name.name' + overwriteType: 'string' + ` + policy, err := tracingpolicy.FromYAML(rawPolicy) + assert.NoError(t, err, "FromYAML rawPolicy error %s", err) + + var btfArgs [api.EventConfigMaxArgs][api.MaxBtfArgDepth]api.ConfigBtfArg + + for i, arg := range policy.TpSpec().LsmHooks[0].Args { + btfArgs[i] = [api.MaxBtfArgDepth]api.ConfigBtfArg{} + lastBtfType, err := buildBtfArg(arg, &btfArgs[i]) + assert.NoError(t, err) + assert.NotEqual(t, nil, lastBtfType) + argType := findTypeFromBtfType(arg, lastBtfType) + assert.NotEqual(t, gt.GenericInvalidType, argType, "Type %s is not supported", (*lastBtfType).TypeName()) + } + + result := [api.EventConfigMaxArgs][api.MaxBtfArgDepth]api.ConfigBtfArg{ + [api.MaxBtfArgDepth]api.ConfigBtfArg{ + {Offset: 16, IsPointer: 1, IsInitialized: 1}, + {Offset: 368, IsPointer: 1, IsInitialized: 1}, + }, + [api.MaxBtfArgDepth]api.ConfigBtfArg{ + {Offset: 64, IsPointer: 1, IsInitialized: 1}, + {Offset: 152, IsPointer: 0, IsInitialized: 1}, + {Offset: 8, IsPointer: 1, IsInitialized: 1}, + {Offset: 32, IsPointer: 0, IsInitialized: 1}, + {Offset: 8, IsPointer: 1, IsInitialized: 1}, + }, + } + assert.Equal(t, btfArgs, result) +} From f817507544b9cccd4472dedb96f4bdb124286219 Mon Sep 17 00:00:00 2001 From: Tristan d'Audibert Date: Wed, 18 Dec 2024 14:56:32 +0100 Subject: [PATCH 14/15] Add new tracingpolicy example for dynamic parameter extraction feature Signed-off-by: Tristan d'Audibert --- examples/tracingpolicy/lsm_track_grandparent.yml | 15 +++++++++++++++ 1 file changed, 15 insertions(+) create mode 100644 examples/tracingpolicy/lsm_track_grandparent.yml diff --git a/examples/tracingpolicy/lsm_track_grandparent.yml b/examples/tracingpolicy/lsm_track_grandparent.yml new file mode 100644 index 00000000000..0553ec64c1a --- /dev/null +++ b/examples/tracingpolicy/lsm_track_grandparent.yml @@ -0,0 +1,15 @@ +apiVersion: cilium.io/v1alpha1 +kind: TracingPolicy +metadata: + name: "lsm" +spec: + lsmhooks: + - hook: "bprm_check_security" + args: + - index: 0 + type: "linux_binprm" + extractParam: "mm.owner.real_parent.real_parent.comm" + overwriteType: "string" + selectors: + - matchActions: + - action: Post From 391c5a8ef90bece1aec0b8e4f7f01fea39d5619d Mon Sep 17 00:00:00 2001 From: Tristan d'Audibert Date: Thu, 19 Dec 2024 13:12:50 +0100 Subject: [PATCH 15/15] Add documentation for extractParam feature Signed-off-by: Tristan d'Audibert --- .../en/docs/concepts/tracing-policy/hooks.md | 55 +++++++++++++++++++ 1 file changed, 55 insertions(+) diff --git a/docs/content/en/docs/concepts/tracing-policy/hooks.md b/docs/content/en/docs/concepts/tracing-policy/hooks.md index 90975cc41b5..80ec4425905 100644 --- a/docs/content/en/docs/concepts/tracing-policy/hooks.md +++ b/docs/content/en/docs/concepts/tracing-policy/hooks.md @@ -400,6 +400,61 @@ The `maxData` flag does not work with `returnCopy` flag at the moment, so it's usable only for syscalls/functions that do not require return probe to read the data. +### Advanced usage + +For specific use cases, you may want to extract a specific attribute from the argument. +For instance you have `struct linux_binprm` as first argument and want to filter parent +process name, you can do it as following. + +```yaml +apiVersion: cilium.io/v1alpha1 +kind: TracingPolicy +metadata: + name: "lsm" +spec: + lsmhooks: + - hook: "bprm_check_security" + args: + - index: 0 + type: "linux_binprm" + extractParam: "mm.owner.real_parent.comm" + overwriteType: "string" + selectors: + - matchActions: + - action: Post +``` + +The above policy will display the parent process name every time the hook is called. +The `extractParam` field is used to reach a specific data into the `struct +linux_binprm`. It is important to set `overwriteType` as well to make sure the +reached data is read correctly (as a string in this case). + +{{< caution >}} +- This feature requires you to know exactly what you are looking for in the attributes +of the hook parameters. For instance, if you want to have a look on what is +available inside `struct linux_binprm`, take a look at the +[Bootlin website](https://elixir.bootlin.com/linux/v6.12.5/source/include/linux/binfmts.h#L18) + +- Some structures are dynamic. This means that they may change at runtime. So you need to +be aware of what you are looking for. +{{< /caution >}} + +Tetragon can also handle some structures such as `struct file` or `struct +path` and few others. This means you can also extract the whole struct, if it is +available in the attributes of the parameter, and set the type with the correct type +like this : + +```yaml + - index: 0 + type: "linux_binprm" + extractParam: "file" + overwriteType: "file" +# Or + # extractParam: "file.f_path" + # overwriteType: "path" +``` + + ## Return values A `TracingPolicy` spec can specify that the return value should be reported in