Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add dynamic extraction of a parameter attribute #3143

Open
wants to merge 15 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 29 additions & 0 deletions bpf/process/generic_calls.h
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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) {
Expand Down
18 changes: 18 additions & 0 deletions bpf/process/types/basic.h
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -172,6 +185,11 @@ struct event_config {
*/
__u32 policy_id;
__u32 flags;
struct config_btf_arg btf_arg0[MAX_BTF_ARG_DEPTH];
Copy link
Contributor

Choose a reason for hiding this comment

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

why not follow the user space and have just btf_arg[5][10]
I don't see btf_arg[1-4] being used anywhere

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];
Copy link
Contributor

Choose a reason for hiding this comment

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

why not follow the user space part and have just btf_arg[5][MAX_BTF_ARG_DEPTH]
I did not see btf_arg[1-4] being touched elsewhere..

} __attribute__((packed));

#define MAX_ARGS_SIZE 80
Expand Down
55 changes: 55 additions & 0 deletions docs/content/en/docs/concepts/tracing-policy/hooks.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Copy link
Member

Choose a reason for hiding this comment

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

I would maybe be more specific on this one, like "extracting structure fields/attribute", whatever you prefer that describes best this feature.


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.
Comment on lines +405 to +407
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
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.
You may want to extract a specific attribute from the argument structure. For instance,
you are hooking a function with `struct linux_binprm` as the first argument and want to
filter the parent process' name, you can do it as follows.


```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).
Comment on lines +427 to +430
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
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).
The above policy will display the parent process's comm every time the hook is called.
The `extractParam` field is used to reach 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).

I would say comm maybe instead of name since this value can be changed by the thread itself, it's not a really safe way of identifying a process name.


{{< 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)
Comment on lines +433 to +436
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
- 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)
- This feature requires you to read the kernel structure definitions to find what you're
looking for in the hook parameter attributes. For instance, if you want to have a look
at what is available inside `struct linux_binprm`, take a look at its definition in
[include/linux/binfmts.h](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.
Comment on lines +438 to +439
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
- Some structures are dynamic. This means that they may change at runtime. So you need to
be aware of what you are looking for.
- Some structures are dynamic which means that they may change at runtime.

{{< /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 :
Comment on lines +442 to +445
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
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 :
Tetragon can also handle some structures such as `struct file` or `struct
path` and a 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"
```

Comment on lines +447 to +456
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
```yaml
- index: 0
type: "linux_binprm"
extractParam: "file"
overwriteType: "file"
# Or
# extractParam: "file.f_path"
# overwriteType: "path"
```
```yaml
- index: 0
type: "linux_binprm"
extractParam: "file"
overwriteType: "file"
```
Or
```yaml
- index: 0
extractParam: "file.f_path"
overwriteType: "path"
```

Mmh not sure my suggestion is correct but I would split it in two.


## Return values

A `TracingPolicy` spec can specify that the return value should be reported in
Expand Down
15 changes: 15 additions & 0 deletions examples/tracingpolicy/lsm_track_grandparent.yml
Original file line number Diff line number Diff line change
@@ -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"
Copy link
Contributor

Choose a reason for hiding this comment

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

do we need overwriteType? could we maybe just do something like:

 - index: 0
   type: "string"
   extractParam: "linux_binprm.file.f_path.dentry.d_name.name"

the overwrite stuff might cause headaches later

also maybe rename extractParam to resolve? not sure ;-)

cc @kkourt

Copy link
Member

Choose a reason for hiding this comment

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

selectors:
- matchActions:
- action: Post
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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: |-
Expand Down Expand Up @@ -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
Expand All @@ -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: |-
Expand Down Expand Up @@ -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
Expand All @@ -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: |-
Expand Down Expand Up @@ -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
Expand All @@ -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: |-
Expand Down Expand Up @@ -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
Expand All @@ -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: |-
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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: |-
Expand Down Expand Up @@ -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
Expand All @@ -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: |-
Expand Down Expand Up @@ -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
Expand All @@ -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: |-
Expand Down Expand Up @@ -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
Expand All @@ -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: |-
Expand Down Expand Up @@ -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
Expand All @@ -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: |-
Expand Down
Loading
Loading