Skip to content

Commit a3f831a

Browse files
committed
feat(driver): add filename parameter to PPME_SYSCALL_EXECVE_19_X
Signed-off-by: Leonardo Di Giovanna <[email protected]>
1 parent 8f257cf commit a3f831a

File tree

19 files changed

+342
-72
lines changed

19 files changed

+342
-72
lines changed

driver/SCHEMA_VERSION

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
4.0.1
1+
4.1.0

driver/bpf/fillers.h

Lines changed: 32 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2856,7 +2856,18 @@ FILLER(execve_extra_tail_2, true) {
28562856
/* Parameter 30: egid (type: PT_GID) */
28572857
struct cred *cred = (struct cred *)_READ(task->cred);
28582858
kgid_t egid = _READ(cred->egid);
2859-
return bpf_push_u32_to_ring(data, egid.val);
2859+
res = bpf_push_u32_to_ring(data, egid.val);
2860+
CHECK_RES(res);
2861+
2862+
if(data->state->tail_ctx.evt_type == PPME_SYSCALL_EXECVEAT_X) {
2863+
return res;
2864+
}
2865+
2866+
/* The following is valid only for PPME_SYSCALL_EXECVE_19_X */
2867+
2868+
/* Parameter 31: filename (type: PT_FSPATH) */
2869+
unsigned long filename_pointer = bpf_syscall_get_argument(data, 0);
2870+
return bpf_val_to_ring_mem(data, filename_pointer, USER);
28602871
}
28612872

28622873
FILLER(sys_accept4_x, true) {
@@ -7120,7 +7131,26 @@ FILLER(sched_prog_exec_5, false) {
71207131
/* Parameter 30: egid (type: PT_GID) */
71217132
struct cred *cred = (struct cred *)_READ(task->cred);
71227133
kgid_t egid = _READ(cred->egid);
7123-
return bpf_push_u32_to_ring(data, egid.val);
7134+
res = bpf_push_u32_to_ring(data, egid.val);
7135+
CHECK_RES(res);
7136+
7137+
/* Parameter 31: filename (type: PT_FSPATH) */
7138+
/* note: in the current implementation, this filler is called for both successful execve and
7139+
* execveat, and always generates an execve event. We use `bprm->filename` to populate the
7140+
* `filename` parameter. `bprm->filename` contains a different thing, depending on the original
7141+
* system call type and arguments provided by the user (see
7142+
* https://elixir.bootlin.com/linux/v6.17.8/source/fs/exec.c#L1422-L1448).
7143+
* At least for execve, it contains the system call's first argument, as provided by the user.
7144+
*/
7145+
struct sched_process_exec_args *original_ctx = (struct sched_process_exec_args *)data->ctx;
7146+
#ifdef BPF_SUPPORTS_RAW_TRACEPOINTS
7147+
struct linux_binprm *bprm = original_ctx->bprm;
7148+
unsigned long filename_pointer = (unsigned long)_READ(bprm->filename);
7149+
#else
7150+
unsigned long filename_offset = original_ctx->__data_loc_filename & 0xFFFF;
7151+
unsigned long filename_pointer = (unsigned long)original_ctx + filename_offset;
7152+
#endif
7153+
return bpf_val_to_ring_mem(data, filename_pointer, KERNEL);
71247154
}
71257155

71267156
#ifdef CAPTURE_SCHED_PROC_FORK

driver/bpf/types.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -154,12 +154,13 @@ struct sched_process_exec_args {
154154
/* TP_PROTO(struct task_struct *p, pid_t old_pid, struct linux_binprm *bprm)
155155
* Taken from `/include/trace/events/sched.h`
156156
*/
157+
#include <linux/binfmts.h> // `struct linux_binprm` definition.
157158
struct sched_process_exec_args {
158159
struct task_struct *p;
159160
pid_t old_pid;
160161
struct linux_binprm *bprm;
161162
};
162-
#endif /* BPF_SUPPORTS_RAW_TRACEPOINTS */
163+
#endif /* BPF_SUPPORTS_RAW_TRACEPOINTS */
163164

164165
#ifdef CAPTURE_SCHED_PROC_FORK
165166
/* TP_PROTO(struct task_struct *parent, struct task_struct *child)

driver/event_table.c

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1981,7 +1981,7 @@ const struct ppm_event_info g_event_info[] = {
19811981
[PPME_SYSCALL_EXECVE_19_X] = {"execve",
19821982
EC_PROCESS | EC_SYSCALL,
19831983
EF_MODIFIES_STATE | EF_CONVERTER_MANAGED,
1984-
30,
1984+
31,
19851985
{{"res", PT_ERRNO, PF_DEC},
19861986
{"exe", PT_CHARBUF, PF_NA},
19871987
{"args", PT_BYTEBUF, PF_NA},
@@ -2011,7 +2011,8 @@ const struct ppm_event_info g_event_info[] = {
20112011
{"uid", PT_UID, PF_DEC},
20122012
{"trusted_exepath", PT_FSPATH, PF_NA},
20132013
{"pgid", PT_PID, PF_NA},
2014-
{"gid", PT_GID, PF_DEC}}},
2014+
{"gid", PT_GID, PF_DEC},
2015+
{"filename", PT_FSPATH, PF_NA}}},
20152016
[PPME_SYSCALL_SETPGID_E] = {"setpgid",
20162017
EC_PROCESS | EC_SYSCALL,
20172018
EF_OLD_VERSION | EF_CONVERTER_MANAGED,

driver/main.c

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -107,16 +107,19 @@ struct event_data_t {
107107
} signal_data;
108108

109109
#ifdef CAPTURE_SCHED_PROC_FORK
110-
/* Here we save only the child task struct since it is the
111-
* unique parameter we will use in our `f_sched_prog_fork`
112-
* filler. On the other side the `f_sched_prog_exec` filler
113-
* won't need any tracepoint parameter so we don't need a
114-
* internal struct here.
110+
/* Here we save only the child task struct since it is the unique parameter we will use in
111+
* our `f_sched_prog_fork` filler.
115112
*/
116113
struct {
117114
struct task_struct *child;
118115
} sched_proc_fork_data;
119116
#endif
117+
/* Here we save only the binprm struct since it is the unique parameter we will use in our
118+
* `f_sched_prog_exec` filler.
119+
*/
120+
struct {
121+
struct linux_binprm *bprm;
122+
} sched_proc_exec_data;
120123

121124
struct fault_data_t fault_data;
122125
} event_info;
@@ -1820,6 +1823,7 @@ static int record_event_consumer(struct ppm_consumer_t *consumer,
18201823
*/
18211824
switch(event_datap->category) {
18221825
case PPMC_SCHED_PROC_EXEC:
1826+
args.sched_proc_exec_bprm = event_datap->event_info.sched_proc_exec_data.bprm;
18231827
cbres = f_sched_prog_exec(&args);
18241828
break;
18251829

@@ -2376,6 +2380,7 @@ TRACEPOINT_PROBE(sched_proc_exec_probe,
23762380
}
23772381

23782382
event_data.category = PPMC_SCHED_PROC_EXEC;
2383+
event_data.event_info.sched_proc_exec_data.bprm = bprm;
23792384
record_event_all_consumers(PPME_SYSCALL_EXECVE_19_X,
23802385
UF_NEVER_DROP,
23812386
&event_data,

driver/modern_bpf/programs/attached/events/sched_process_exec.bpf.c

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -291,7 +291,7 @@ int BPF_PROG(t1_sched_p_exec, struct task_struct *p, pid_t old_pid, struct linux
291291
}
292292

293293
SEC("tp_btf/sched_process_exec")
294-
int BPF_PROG(t2_sched_p_exec, struct pt_regs *regs, long ret) {
294+
int BPF_PROG(t2_sched_p_exec, struct pt_regs *regs, long ret, struct linux_binprm *bprm) {
295295
struct auxiliary_map *auxmap = auxmap__get();
296296
if(!auxmap) {
297297
return 0;
@@ -317,6 +317,21 @@ int BPF_PROG(t2_sched_p_exec, struct pt_regs *regs, long ret) {
317317
extract__egid(task, &egid);
318318
auxmap__store_u32_param(auxmap, egid);
319319

320+
/* Parameter 31: filename (type: PT_FSPATH) */
321+
/* note: in the current implementation, this program is called for both execve and execveat, and
322+
* always generates an execve event. We use `bprm->filename` to populate the `filename`
323+
* parameter. `bprm->filename` contains a different thing, depending on the original system call
324+
* type and arguments provided by the user (see
325+
* https://elixir.bootlin.com/linux/v6.17.8/source/fs/exec.c#L1422-L1448).
326+
* At least for execve, it contains the system call's first argument, as provided by the user.
327+
*/
328+
unsigned long filename_pointer = (unsigned long)BPF_CORE_READ(bprm, filename);
329+
if(!filename_pointer) {
330+
auxmap__store_empty_param(auxmap);
331+
} else {
332+
auxmap__store_charbuf_param(auxmap, filename_pointer, MAX_PATH, KERNEL);
333+
}
334+
320335
/*=============================== COLLECT PARAMETERS ===========================*/
321336

322337
auxmap__finalize_event_header(auxmap);

driver/modern_bpf/programs/tail_called/events/syscall_dispatched_events/execve.bpf.c

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -288,6 +288,10 @@ int BPF_PROG(t2_execve_x, struct pt_regs *regs, long ret) {
288288
extract__egid(task, &egid);
289289
auxmap__store_u32_param(auxmap, egid);
290290

291+
/* Parameter 31: filename (type: PT_FSPATH) */
292+
unsigned long filename_pointer = extract__syscall_argument(regs, 0);
293+
auxmap__store_charbuf_param(auxmap, filename_pointer, MAX_PATH, USER);
294+
291295
/*=============================== COLLECT PARAMETERS ===========================*/
292296

293297
auxmap__finalize_event_header(auxmap);

driver/ppm_events.h

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,11 +13,11 @@ or GPL2.txt for full copies of the license.
1313

1414
/* To know about __NR_socketcall */
1515
#include <asm/unistd.h>
16-
#include "ppm_consumer.h"
16+
#include <linux/binfmts.h> // `struct linux_binprm` definition.
1717
#ifdef CONFIG_COMPAT
1818
#include <linux/compat.h>
1919
#endif
20-
20+
#include "ppm_consumer.h"
2121
#include "ppm_events_public.h"
2222

2323
/*
@@ -56,6 +56,7 @@ struct event_filler_arguments {
5656
#ifdef CAPTURE_SCHED_PROC_FORK
5757
struct task_struct *child; /* for sched_process_fork events, this is the child task */
5858
#endif
59+
struct linux_binprm *sched_proc_exec_bprm; /* Only meaningful for sched_process_exec events. */
5960

6061
char *str_storage; /* String storage. Size is one page. */
6162
unsigned long args[6];

driver/ppm_fillers.c

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1344,6 +1344,14 @@ int f_proc_startupdate(struct event_filler_arguments *args) {
13441344
egid = from_kgid_munged(current_user_ns(), current_egid());
13451345
res = val_to_ring(args, egid, 0, false, 0);
13461346
CHECK_RES(res);
1347+
1348+
if(args->event_type == PPME_SYSCALL_EXECVE_19_X) {
1349+
/* Parameter 31: filename (type: PT_FSPATH) */
1350+
unsigned long filename_pointer;
1351+
syscall_get_arguments_deprecated(args, 0, 1, &filename_pointer);
1352+
res = val_to_ring(args, filename_pointer, 0, true, 0);
1353+
CHECK_RES(res);
1354+
}
13471355
}
13481356
return add_sentinel(args);
13491357
}
@@ -7545,6 +7553,7 @@ int f_sched_prog_exec(struct event_filler_arguments *args) {
75457553
uint32_t egid = UINT32_MAX;
75467554
char *buf = (char *)args->str_storage;
75477555
char *trusted_exepath = NULL;
7556+
unsigned long filename_pointer = 0;
75487557

75497558
/* Parameter 1: res (type: PT_ERRNO) */
75507559
/* Please note: if this filler is called the execve is correctly
@@ -7865,6 +7874,18 @@ int f_sched_prog_exec(struct event_filler_arguments *args) {
78657874
res = val_to_ring(args, egid, 0, false, 0);
78667875
CHECK_RES(res);
78677876

7877+
/* Parameter 31: filename (type: PT_FSPATH) */
7878+
/* note: in the current implementation, this filler is called for both execve and execveat, and
7879+
* always generates an execve event. We use `bprm->filename` to populate the `filename`
7880+
* parameter. `bprm->filename` contains a different thing, depending on the original system call
7881+
* type and arguments provided by the user (see
7882+
* https://elixir.bootlin.com/linux/v6.17.8/source/fs/exec.c#L1422-L1448).
7883+
* At least for execve, it contains the system call's first argument, as provided by the user.
7884+
*/
7885+
filename_pointer = (unsigned long)args->sched_proc_exec_bprm->filename;
7886+
res = val_to_ring(args, filename_pointer, 0, false, 0);
7887+
CHECK_RES(res);
7888+
78687889
return add_sentinel(args);
78697890
}
78707891

test/drivers/test_suites/generic_tracepoints_suite/sched_process_exec.cpp

Lines changed: 44 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -264,9 +264,12 @@ TEST(GenericTracepoints, sched_proc_exec_execve) {
264264
/* Parameter 30: egid (type: PT_GID) */
265265
evt_test->assert_numeric_param(30, (uint32_t)getegid(), EQUAL);
266266

267+
/* Parameter 31: filename (type: PT_FSPATH) */
268+
evt_test->assert_charbuf_param(31, pathname);
269+
267270
/*=============================== ASSERT PARAMETERS ===========================*/
268271

269-
evt_test->assert_num_params_pushed(30);
272+
evt_test->assert_num_params_pushed(31);
270273
}
271274

272275
#if defined(__NR_memfd_create) && defined(__NR_openat) && defined(__NR_read) && defined(__NR_write)
@@ -378,9 +381,14 @@ TEST(GenericTracepoints, sched_proc_exec_execve_memfd) {
378381
evt_test->assert_charbuf_param(28, "memfd:malware");
379382
}
380383

384+
/* Parameter 31: filename (type: PT_FSPATH) */
385+
char pathname[200];
386+
snprintf(pathname, sizeof(pathname), "/proc/%d/fd/%d", ret_pid, mem_fd);
387+
evt_test->assert_charbuf_param(31, pathname);
388+
381389
/*=============================== ASSERT PARAMETERS ===========================*/
382390

383-
evt_test->assert_num_params_pushed(30);
391+
evt_test->assert_num_params_pushed(31);
384392
}
385393
#endif
386394

@@ -510,9 +518,12 @@ TEST(GenericTracepoints, sched_proc_exec_execve_not_upperlayer) {
510518
/* Parameter 30: egid (type: PT_GID) */
511519
evt_test->assert_numeric_param(30, (uint32_t)getegid(), EQUAL);
512520

521+
/* Parameter 31: filename (type: PT_FSPATH) */
522+
evt_test->assert_charbuf_param(31, merged_exe_path);
523+
513524
/*=============================== ASSERT PARAMETERS ===========================*/
514525

515-
evt_test->assert_num_params_pushed(30);
526+
evt_test->assert_num_params_pushed(31);
516527
}
517528

518529
TEST(GenericTracepoints, sched_proc_exec_execve_upperlayer) {
@@ -640,9 +651,12 @@ TEST(GenericTracepoints, sched_proc_exec_execve_upperlayer) {
640651
/* Parameter 30: egid (type: PT_GID) */
641652
evt_test->assert_numeric_param(30, (uint32_t)getegid(), EQUAL);
642653

654+
/* Parameter 31: filename (type: PT_FSPATH) */
655+
evt_test->assert_charbuf_param(31, pathname);
656+
643657
/*=============================== ASSERT PARAMETERS ===========================*/
644658

645-
evt_test->assert_num_params_pushed(30);
659+
evt_test->assert_num_params_pushed(31);
646660
}
647661

648662
#if defined(__NR_symlinkat) && defined(__NR_unlinkat)
@@ -734,9 +748,12 @@ TEST(GenericTracepoints, sched_proc_exec_execve_symlink) {
734748
/* Parameter 28: resolve_path (type: PT_CHARBUF) */
735749
evt_test->assert_charbuf_param(28, pathname);
736750

751+
/* Parameter 31: filename (type: PT_FSPATH) */
752+
evt_test->assert_charbuf_param(31, linkpath);
753+
737754
/*=============================== ASSERT PARAMETERS ===========================*/
738755

739-
evt_test->assert_num_params_pushed(30);
756+
evt_test->assert_num_params_pushed(31);
740757
}
741758
#endif
742759

@@ -820,7 +837,7 @@ TEST(GenericTracepoints, sched_proc_exec_execveat) {
820837

821838
/* Please note here we cannot assert all the params, we check only the possible ones. */
822839

823-
/* Parameter 1: res (type: PT_ERRNO)*/
840+
/* Parameter 1: res (type: PT_ERRNO) */
824841
evt_test->assert_numeric_param(1, (int64_t)0);
825842

826843
/* Parameter 2: exe (type: PT_CHARBUF) */
@@ -899,9 +916,12 @@ TEST(GenericTracepoints, sched_proc_exec_execveat) {
899916
/* Parameter 30: egid (type: PT_GID) */
900917
evt_test->assert_numeric_param(30, (uint32_t)getegid(), EQUAL);
901918

919+
/* Parameter 31: filename (type: PT_FSPATH) */
920+
evt_test->assert_charbuf_param(31, pathname);
921+
902922
/*=============================== ASSERT PARAMETERS ===========================*/
903923

904-
evt_test->assert_num_params_pushed(30);
924+
evt_test->assert_num_params_pushed(31);
905925
}
906926

907927
#if defined(__NR_memfd_create) && defined(__NR_openat) && defined(__NR_read) && defined(__NR_write)
@@ -1013,14 +1033,19 @@ TEST(GenericTracepoints, sched_proc_exec_execveat_memfd) {
10131033
evt_test->assert_charbuf_param(28, "memfd:malware");
10141034
}
10151035

1036+
/* Parameter 31: filename (type: PT_FSPATH) */
1037+
char pathname[200];
1038+
snprintf(pathname, sizeof(pathname), "/proc/%d/fd/%d", ret_pid, mem_fd);
1039+
evt_test->assert_charbuf_param(31, pathname);
1040+
10161041
/*=============================== ASSERT PARAMETERS ===========================*/
10171042

1018-
evt_test->assert_num_params_pushed(30);
1043+
evt_test->assert_num_params_pushed(31);
10191044
}
10201045
#endif // defined(__NR_memfd_create) && defined(__NR_openat) && defined(__NR_read) &&
10211046
// defined(__NR_write)
10221047

1023-
TEST(GenericTracepoints, sched_proc_exec_execveat_comm_equal_to_fd) {
1048+
TEST(GenericTracepoints, sched_proc_exec_execveat_comm_equal_to_fd_in_old_kernel_versions) {
10241049
auto evt_test = get_syscall_event_test(__NR_execve, EXIT_EVENT);
10251050

10261051
evt_test->enable_capture();
@@ -1036,9 +1061,11 @@ TEST(GenericTracepoints, sched_proc_exec_execveat_comm_equal_to_fd) {
10361061

10371062
// We will use the `AT_EMPTY_PATH` strategy
10381063
const char *pathname = "";
1039-
const char *argv[] = {pathname,
1040-
"[OUTPUT] GenericTracepoints.sched_proc_exec_execveat_comm_equal_to_fd",
1041-
NULL};
1064+
const char *argv[] = {
1065+
pathname,
1066+
"[OUTPUT] "
1067+
"GenericTracepoints.sched_proc_exec_execveat_comm_equal_to_fd_in_old_kernel_versions",
1068+
NULL};
10421069
const char *envp[] = {"IN_TEST=yes", "3_ARGUMENT=yes", "2_ARGUMENT=no", NULL};
10431070
int flags = AT_EMPTY_PATH;
10441071

@@ -1114,9 +1141,13 @@ TEST(GenericTracepoints, sched_proc_exec_execveat_comm_equal_to_fd) {
11141141
/* Parameter 28: trusted_exepath (type: PT_FSPATH) */
11151142
evt_test->assert_charbuf_param(28, exe_path.c_str());
11161143

1144+
/* Parameter 31: filename (type: PT_FSPATH) */
1145+
const std::string dirfd_path{"/dev/fd/" + dirfd_str};
1146+
evt_test->assert_charbuf_param(31, dirfd_path.c_str());
1147+
11171148
/*=============================== ASSERT PARAMETERS ===========================*/
11181149

1119-
evt_test->assert_num_params_pushed(30);
1150+
evt_test->assert_num_params_pushed(31);
11201151
}
11211152

11221153
#endif // __NR_execveat

0 commit comments

Comments
 (0)