Skip to content

Commit 31e7a3f

Browse files
committed
feat: isolate modern probe TOCTOU mitigation logic
Current TOCTOU mitigation implementation leverages the enter events generated by eBPF programs which are tail-called by the `sys_enter` dispatcher. As the architecture is moving towards dropping enter event generation and collection, the `sys_enter` dispatcher will be ultimately removed. This requires to isolate TOCTOU mitigation handling logic in order to make it independent from `sys_enter` dispatcher removal. This patch moves TOCTOU mitigation handling logic into separate ad-hoc eBPF tracepoint programs attached on corresponding `syscalls/sys_enter_*` hooks. For each system call `<syscall>` that already supports TOCTOU mitigation, two eBPF tracepoint programs are defined: - `ttm_<syscall>_e` - this program is responsible for tail calling the `<syscall>_e` program after having performed syscall ID normalization and applied any required sampling/filtering logic - `<syscall>_e` - this program is responsible for collecting the information needed to generate the proper enter event and sending the generated event to userspace Tail-called TOCTOU mitigation programs are inserted into a separate tail call map, called `syscall_enter_toctou_mitigation_tail_table`. As the tracepoint attachment procedure generates an `openat` exit event on `/sys/kernel/tracing/events/.../id` that would pollute the stream of events read by the probe, the implementation takes care of attaching them before attaching the `sys_exit` dispatcher. Notice that the new logic make the TOCTOU mitigation programs attachment dependent on the presence of the `sys_exit` dispatcher. Signed-off-by: Leonardo Di Giovanna <[email protected]>
1 parent 082157d commit 31e7a3f

File tree

22 files changed

+1028
-516
lines changed

22 files changed

+1028
-516
lines changed

driver/modern_bpf/helpers/interfaces/syscalls_dispatcher.h

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,36 @@
1313
#include <helpers/base/read_from_task.h>
1414
#include <helpers/extract/extract_from_kernel.h>
1515

16+
// We don't want to send DROP_E/DROP_X events from the enter tracepoint because it would requires us
17+
// to create a dedicated tail table for the enter. It is enough to send DROP_E/DROP_X events from
18+
// the exit tracepoint.
19+
static __always_inline bool syscalls_dispatcher__sampling_logic_enter(uint32_t syscall_id) {
20+
/* If dropping mode is not enabled we don't perform any sampling
21+
* false: means don't drop the syscall
22+
* true: means drop the syscall
23+
*/
24+
if(!maps__get_dropping_mode()) {
25+
return false;
26+
}
27+
28+
uint8_t sampling_flag = maps__64bit_sampling_syscall_table(syscall_id);
29+
30+
if(sampling_flag == UF_NEVER_DROP) {
31+
return false;
32+
}
33+
34+
if(sampling_flag == UF_ALWAYS_DROP) {
35+
return true;
36+
}
37+
38+
// If we are in the sampling period we drop the event
39+
if((bpf_ktime_get_boot_ns() % SECOND_TO_NS) >= (SECOND_TO_NS / maps__get_sampling_ratio())) {
40+
return true;
41+
}
42+
43+
return false;
44+
}
45+
1646
static __always_inline bool syscalls_dispatcher__64bit_interesting_syscall(uint32_t syscall_id) {
1747
return maps__interesting_syscall_64bit(syscall_id);
1848
}
Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
// SPDX-License-Identifier: GPL-2.0-only OR MIT
2+
/*
3+
* Copyright (C) 2025 The Falco Authors.
4+
*
5+
* This file is dual licensed under either the MIT or GPL 2. See MIT.txt
6+
* or GPL2.txt for full copies of the license.
7+
*/
8+
9+
#pragma once
10+
11+
#include <helpers/interfaces/syscalls_dispatcher.h>
12+
13+
/**
14+
* @brief Tail call the TOCTOU mitigation program corresponding to the specified program code.
15+
*
16+
* @param ctx is the original program context
17+
* @param syscall_id is the original system call id that triggered the program
18+
* @param socketcall_network_syscall_id (network system calls only) is the id of the equivalent
19+
* network system call issued through socketcall (e.g.: __NR_connect)
20+
* @param prog_code is the program code identifying the TOCTOU mitigation program to be called
21+
22+
* @return never returns in case of success; otherwise, returns 0
23+
*/
24+
static __always_inline int toctou_mitigation__call_prog(
25+
void *ctx,
26+
uint32_t syscall_id,
27+
uint32_t socketcall_network_syscall_id,
28+
enum sys_enter_toctou_mitigation_prog_code prog_code) {
29+
int socketcall_syscall_id = -1;
30+
if(bpf_in_ia32_syscall()) {
31+
#if defined(__TARGET_ARCH_x86)
32+
if(syscall_id == __NR_ia32_socketcall) {
33+
socketcall_syscall_id = __NR_ia32_socketcall;
34+
} else {
35+
syscall_id = maps__ia32_to_64(syscall_id);
36+
// Syscalls defined only on 32 bits are dropped here.
37+
if(syscall_id == (uint32_t)-1) {
38+
return 0;
39+
}
40+
}
41+
#else
42+
return 0;
43+
#endif
44+
} else {
45+
#ifdef __NR_socketcall
46+
socketcall_syscall_id = __NR_socketcall;
47+
#endif
48+
}
49+
50+
// Convert the socketcall id into the network syscall id.
51+
// In this way the syscall will be treated exactly as the original one.
52+
if(syscall_id == socketcall_syscall_id) {
53+
syscall_id = socketcall_network_syscall_id;
54+
if(syscall_id == -1) {
55+
// We can't do anything since modern bpf filler jump table is syscall indexed.
56+
return 0;
57+
}
58+
}
59+
60+
if(!syscalls_dispatcher__64bit_interesting_syscall(syscall_id)) {
61+
return 0;
62+
}
63+
64+
if(syscalls_dispatcher__sampling_logic_enter(syscall_id)) {
65+
return 0;
66+
}
67+
68+
bpf_tail_call(ctx, &syscall_enter_toctou_mitigation_tail_table, prog_code);
69+
bpf_printk("unable to tail call into TTM prog (prog_code: %d)", prog_code);
70+
return 0;
71+
}

driver/modern_bpf/maps/maps.h

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,18 @@ struct {
104104
__type(value, uint32_t);
105105
} syscall_exit_tail_table __weak SEC(".maps");
106106

107+
/**
108+
* @brief This tail table is used by the TOCTOU mitigation sys_enter_* programs.
109+
* Given a predefined tail-code (`sys_enter_toctou_mitigation_prog_code`), they call
110+
* the right bpf program to generate the proper TOCTOU mitigation event.
111+
*/
112+
struct {
113+
__uint(type, BPF_MAP_TYPE_PROG_ARRAY);
114+
__uint(max_entries, SYS_ENTER_TOCTOU_MITIGATION_PROG_CODE_MAX);
115+
__type(key, uint32_t);
116+
__type(value, uint32_t);
117+
} syscall_enter_toctou_mitigation_tail_table __weak SEC(".maps");
118+
107119
/**
108120
* @brief This tail table is used when a sys exit bpf program needs another program
109121
* to complete its execution flow.

driver/modern_bpf/programs/attached/dispatchers/syscall_enter.bpf.c

Lines changed: 24 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -8,36 +8,6 @@
88

99
#include <helpers/interfaces/syscalls_dispatcher.h>
1010

11-
// We don't want to send DROP_E/DROP_X events from the enter tracepoint because it would requires us
12-
// to create a dedicated tail table for the enter. It is enough to send DROP_E/DROP_X events from
13-
// the exit tracepoint.
14-
static __always_inline bool sampling_logic_enter(void* ctx, uint32_t id) {
15-
/* If dropping mode is not enabled we don't perform any sampling
16-
* false: means don't drop the syscall
17-
* true: means drop the syscall
18-
*/
19-
if(!maps__get_dropping_mode()) {
20-
return false;
21-
}
22-
23-
uint8_t sampling_flag = maps__64bit_sampling_syscall_table(id);
24-
25-
if(sampling_flag == UF_NEVER_DROP) {
26-
return false;
27-
}
28-
29-
if(sampling_flag == UF_ALWAYS_DROP) {
30-
return true;
31-
}
32-
33-
// If we are in the sampling period we drop the event
34-
if((bpf_ktime_get_boot_ns() % SECOND_TO_NS) >= (SECOND_TO_NS / maps__get_sampling_ratio())) {
35-
return true;
36-
}
37-
38-
return false;
39-
}
40-
4111
/* From linux tree: /include/trace/events/syscall.h
4212
* TP_PROTO(struct pt_regs *regs, long id),
4313
*/
@@ -74,11 +44,34 @@ int BPF_PROG(sys_enter, struct pt_regs* regs, long syscall_id) {
7444
}
7545
}
7646

47+
// The following system calls are already handled by TOCTOU mitigation programs and will not
48+
// have an entry in the syscall enter tail table, so simply return early, avoiding wasting
49+
// resources on any additional filtering logic.
50+
switch(syscall_id) {
51+
#if defined(__NR_connect) || defined(__NR_creat) || defined(__NR_open) || defined(__NR_openat)
52+
#ifdef __NR_connect
53+
case __NR_connect:
54+
#endif // __NR_connect
55+
#ifdef __NR_creat
56+
case __NR_creat:
57+
#endif // __NR_creat
58+
#ifdef __NR_open
59+
case __NR_open:
60+
#endif // __NR_open
61+
#ifdef __NR_openat
62+
case __NR_openat:
63+
#endif // __NR_openat
64+
return 0;
65+
#endif // __NR_connect ||__NR_creat || __NR_open || __NR_openat
66+
default:
67+
break;
68+
}
69+
7770
if(!syscalls_dispatcher__64bit_interesting_syscall(syscall_id)) {
7871
return 0;
7972
}
8073

81-
if(sampling_logic_enter(ctx, syscall_id)) {
74+
if(syscalls_dispatcher__sampling_logic_enter(syscall_id)) {
8275
return 0;
8376
}
8477

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
# TOCTOU mitigation programs
2+
3+
eBPF programs in this folder generates enter events providing support to exit events data for TOCTOU mitigation.
4+
5+
All programs are attached to specific `tracepoint/syscalls/sys_enter_*` hooks.
6+
7+
For each system call `<syscall>`, two eBPF tracepoint programs are defined:
8+
- `ttm_<syscall>_e` - this program is responsible for tail calling the `<syscall>_e` program after having performed
9+
syscall ID normalization and applied any required sampling/filtering logic
10+
- `<syscall>_e` - this program is responsible for collecting the information needed to generate the proper enter event
11+
and sending the generated event to userspace
Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
// SPDX-License-Identifier: GPL-2.0-only OR MIT
2+
/*
3+
* Copyright (C) 2025 The Falco Authors.
4+
*
5+
* This file is dual licensed under either the MIT or GPL 2. See MIT.txt
6+
* or GPL2.txt for full copies of the license.
7+
*/
8+
9+
#include <helpers/interfaces/toctou_mitigation.h>
10+
#include <helpers/interfaces/variable_size_event.h>
11+
12+
// This struct is defined according to the following format file:
13+
// /sys/kernel/tracing/events/syscalls/sys_enter_connect/format
14+
struct sys_enter_connect_args {
15+
uint64_t pad1;
16+
17+
uint32_t __syscall_nr;
18+
uint32_t pad2;
19+
uint64_t fd;
20+
uint64_t uservaddr;
21+
uint64_t addrlen;
22+
};
23+
24+
/*=============================== ENTER EVENT ===========================*/
25+
26+
SEC("tracepoint/syscalls/sys_enter_connect")
27+
int connect_e(struct sys_enter_connect_args* ctx) {
28+
#ifdef __NR_connect
29+
uint32_t socketcall_network_syscall_id = __NR_connect;
30+
#else
31+
uint32_t socketcall_network_syscall_id = -1;
32+
#endif
33+
return toctou_mitigation__call_prog(ctx,
34+
ctx->__syscall_nr,
35+
socketcall_network_syscall_id,
36+
TTM_CONNECT_E);
37+
}
38+
39+
SEC("tracepoint/syscalls/sys_enter_connect")
40+
int ttm_connect_e(struct sys_enter_connect_args* ctx) {
41+
struct auxiliary_map* auxmap = auxmap__get();
42+
if(!auxmap) {
43+
return 0;
44+
}
45+
auxmap__preload_event_header(auxmap, PPME_SOCKET_CONNECT_E);
46+
47+
/*=============================== COLLECT PARAMETERS ===========================*/
48+
49+
/* Parameter 1: fd (type: PT_FD) */
50+
int64_t socket_fd = (int64_t)(int32_t)ctx->fd;
51+
auxmap__store_s64_param(auxmap, socket_fd);
52+
53+
/* Parameter 2: addr (type: PT_SOCKADDR) */
54+
unsigned long usrsockaddr = (unsigned long)ctx->uservaddr;
55+
uint16_t usrsockaddr_len = (uint16_t)ctx->addrlen;
56+
auxmap__store_sockaddr_param(auxmap, usrsockaddr, usrsockaddr_len);
57+
58+
/*=============================== COLLECT PARAMETERS ===========================*/
59+
60+
auxmap__finalize_event_header(auxmap);
61+
62+
auxmap__submit_event(auxmap);
63+
64+
return 0;
65+
}
66+
67+
/*=============================== ENTER EVENT ===========================*/
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
// SPDX-License-Identifier: GPL-2.0-only OR MIT
2+
/*
3+
* Copyright (C) 2025 The Falco Authors.
4+
*
5+
* This file is dual licensed under either the MIT or GPL 2. See MIT.txt
6+
* or GPL2.txt for full copies of the license.
7+
*/
8+
9+
#include <helpers/interfaces/toctou_mitigation.h>
10+
#include <helpers/interfaces/variable_size_event.h>
11+
12+
// This struct is defined according to the following format file:
13+
// /sys/kernel/tracing/events/syscalls/sys_enter_creat/format
14+
struct sys_enter_creat_args {
15+
uint64_t pad;
16+
17+
uint32_t __syscall_nr;
18+
uint64_t filename;
19+
uint64_t mode;
20+
};
21+
22+
/*=============================== ENTER EVENT ===========================*/
23+
24+
SEC("tracepoint/syscalls/sys_enter_creat")
25+
int creat_e(struct sys_enter_creat_args* ctx) {
26+
return toctou_mitigation__call_prog(ctx, ctx->__syscall_nr, -1, TTM_CREAT_E);
27+
}
28+
29+
SEC("tracepoint/syscalls/sys_enter_creat")
30+
int ttm_creat_e(struct sys_enter_creat_args* ctx) {
31+
struct auxiliary_map* auxmap = auxmap__get();
32+
if(!auxmap) {
33+
return 0;
34+
}
35+
36+
auxmap__preload_event_header(auxmap, PPME_SYSCALL_CREAT_E);
37+
38+
/*=============================== COLLECT PARAMETERS ===========================*/
39+
40+
/* Parameter 1: name (type: PT_FSPATH) */
41+
unsigned long name_pointer = (unsigned long)ctx->filename;
42+
auxmap__store_charbuf_param(auxmap, name_pointer, MAX_PATH, USER);
43+
44+
/* Parameter 2: mode (type: PT_UINT32) */
45+
unsigned long mode = (unsigned long)ctx->mode;
46+
auxmap__store_u32_param(auxmap, open_modes_to_scap(O_CREAT, mode));
47+
48+
/*=============================== COLLECT PARAMETERS ===========================*/
49+
50+
auxmap__finalize_event_header(auxmap);
51+
52+
auxmap__submit_event(auxmap);
53+
54+
return 0;
55+
}
56+
57+
/*=============================== ENTER EVENT ===========================*/

0 commit comments

Comments
 (0)