Skip to content

Commit fa52e66

Browse files
committed
ROX-29474: StackRox fixes for 0.23.1 upgrade
- Fix BPF verifier failures on older kernels (4.18) - Use volatile on push__bytebuf len_to_read for 32-bit bounds - Stub out t1/t2_execveat_x (not subscribed by collector) - Use BPF_CORE_READ_INTO for cred reads - Disable TOCTOU 64-bit progs for missing syscalls (e.g. openat2) - Skip fd-based execs (/dev/fd/N) in exepath fallback - Fix exepath resolution without enter events (bprm->filename) Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1 parent 2291f61 commit fa52e66

File tree

9 files changed

+155
-39
lines changed

9 files changed

+155
-39
lines changed

driver/modern_bpf/CMakeLists.txt

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -260,11 +260,19 @@ file(GLOB_RECURSE BPF_C_FILES CONFIGURE_DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/*.bp
260260
# Generate the events dimensions file generator executable.
261261
# ##################################################################################################
262262

263+
# Compile driver sources directly instead of linking scap_event_schema to
264+
# avoid a cyclic dependency: scap_event_schema -> scap -> pman ->
265+
# ProbeSkeleton -> EventsDimensions -> events_dimensions_generator.
263266
add_executable(
264-
events_dimensions_generator ${CMAKE_CURRENT_SOURCE_DIR}/definitions/generator/generator.cpp
267+
events_dimensions_generator
268+
${CMAKE_CURRENT_SOURCE_DIR}/definitions/generator/generator.cpp
269+
${LIBS_DIR}/driver/event_table.c
270+
${LIBS_DIR}/driver/flags_table.c
271+
${LIBS_DIR}/driver/dynamic_params_table.c
272+
)
273+
target_include_directories(events_dimensions_generator PRIVATE
274+
${LIBS_DIR} ${LIBS_DIR}/userspace ${PROJECT_BINARY_DIR}
265275
)
266-
target_link_libraries(events_dimensions_generator PRIVATE scap_event_schema)
267-
add_dependencies(events_dimensions_generator scap_event_schema)
268276

269277
# ##################################################################################################
270278
# Generate the events dimensions file.

driver/modern_bpf/helpers/extract/extract_from_kernel.h

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -380,7 +380,7 @@ static __always_inline uint64_t extract__capability(struct task_struct *task,
380380
unsigned long capability;
381381
const struct cred *task_cred;
382382

383-
READ_TASK_FIELD_INTO(&task_cred, task, cred);
383+
BPF_CORE_READ_INTO(&task_cred, task, cred);
384384

385385
if(task_cred == NULL)
386386
return 0;
@@ -744,7 +744,7 @@ static __always_inline void extract__euid(struct task_struct *task, uint32_t *eu
744744
const struct cred *task_cred;
745745
*euid = UINT32_MAX;
746746

747-
READ_TASK_FIELD_INTO(&task_cred, task, cred);
747+
BPF_CORE_READ_INTO(&task_cred, task, cred);
748748

749749
if(task_cred == NULL)
750750
return;
@@ -760,8 +760,9 @@ static __always_inline void extract__euid(struct task_struct *task, uint32_t *eu
760760
*/
761761
static __always_inline void extract__egid(struct task_struct *task, uint32_t *egid) {
762762
const struct cred *task_cred;
763+
*egid = UINT32_MAX;
763764

764-
READ_TASK_FIELD_INTO(&task_cred, task, cred);
765+
BPF_CORE_READ_INTO(&task_cred, task, cred);
765766

766767
if(task_cred == NULL)
767768
return;
@@ -914,7 +915,7 @@ static __always_inline bool groups_search(struct task_struct *task, uint32_t grp
914915
struct group_info *group_info = NULL;
915916
const struct cred *task_cred;
916917

917-
READ_TASK_FIELD_INTO(&task_cred, task, cred);
918+
BPF_CORE_READ_INTO(&task_cred, task, cred);
918919

919920
if(task_cred == NULL)
920921
return false;
@@ -970,11 +971,11 @@ static __always_inline bool extract__exe_writable(struct task_struct *task, stru
970971
uint32_t fsgid;
971972
const struct cred *task_cred;
972973

973-
READ_TASK_FIELD_INTO(&task_cred, task, cred);
974+
BPF_CORE_READ_INTO(&task_cred, task, cred);
974975

975976
if(task_cred != NULL) {
976-
READ_TASK_FIELD_INTO(&fsuid, task_cred, fsuid.val);
977-
READ_TASK_FIELD_INTO(&fsgid, task_cred, fsgid.val);
977+
BPF_CORE_READ_INTO(&fsuid, task_cred, fsuid.val);
978+
BPF_CORE_READ_INTO(&fsgid, task_cred, fsgid.val);
978979
}
979980

980981
/* HAS_UNMAPPED_ID() */

driver/modern_bpf/helpers/interfaces/toctou_mitigation.h

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,19 @@ static __always_inline bool toctou_mitigation__sampling_logic_enter(uint32_t sys
1616
* - false: means don't drop the syscall
1717
* - true: means drop the syscall
1818
*/
19-
if(!maps__get_dropping_mode()) {
19+
20+
/* Do a single map lookup for capture_settings and access fields
21+
* directly. Multiple inlined calls to maps__get_capture_settings()
22+
* can cause clang to optimize away null checks after the first
23+
* lookup, which the BPF verifier rejects on kernels < 6.17 with
24+
* "R0 invalid mem access 'map_value_or_null'".
25+
*/
26+
struct capture_settings *settings = maps__get_capture_settings();
27+
if(!settings) {
28+
return false;
29+
}
30+
31+
if(!settings->dropping_mode) {
2032
return false;
2133
}
2234

@@ -31,7 +43,7 @@ static __always_inline bool toctou_mitigation__sampling_logic_enter(uint32_t sys
3143
}
3244

3345
// 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())) {
46+
if((bpf_ktime_get_boot_ns() % SECOND_TO_NS) >= (SECOND_TO_NS / settings->sampling_ratio)) {
3547
return true;
3648
}
3749

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

Lines changed: 22 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -122,12 +122,18 @@ struct {
122122
},
123123
};
124124

125-
static __always_inline bool sampling_logic_exit(void *ctx, uint32_t id) {
125+
/* Accept a pre-looked-up capture_settings pointer to avoid multiple
126+
* bpf_map_lookup_elem calls within the same BPF program. Clang can
127+
* optimize away null checks on repeated lookups, which the BPF verifier
128+
* rejects on kernels < 6.17 with "R0 invalid mem access 'map_value_or_null'".
129+
*/
130+
static __always_inline bool sampling_logic_exit(void *ctx, uint32_t id,
131+
struct capture_settings *settings) {
126132
/* If dropping mode is not enabled we don't perform any sampling
127133
* false: means don't drop the syscall
128134
* true: means drop the syscall
129135
*/
130-
if(!maps__get_dropping_mode()) {
136+
if(!settings->dropping_mode) {
131137
return false;
132138
}
133139

@@ -141,7 +147,7 @@ static __always_inline bool sampling_logic_exit(void *ctx, uint32_t id) {
141147
return true;
142148
}
143149

144-
if((bpf_ktime_get_boot_ns() % SECOND_TO_NS) >= (SECOND_TO_NS / maps__get_sampling_ratio())) {
150+
if((bpf_ktime_get_boot_ns() % SECOND_TO_NS) >= (SECOND_TO_NS / settings->sampling_ratio)) {
145151
/* If we are starting the dropping phase we need to notify the userspace, otherwise, we
146152
* simply drop our event.
147153
* PLEASE NOTE: this logic is not per-CPU so it is best effort!
@@ -221,11 +227,22 @@ int BPF_PROG(sys_exit, struct pt_regs *regs, long ret) {
221227
return 0;
222228
}
223229

224-
if(sampling_logic_exit(ctx, syscall_id)) {
230+
/* Do a single map lookup for capture_settings and reuse the pointer
231+
* for both sampling_logic_exit() and drop_failed check. This avoids
232+
* multiple bpf_map_lookup_elem calls whose null checks can be
233+
* optimized away by clang, causing BPF verifier failures on
234+
* kernels < 6.17.
235+
*/
236+
struct capture_settings *settings = maps__get_capture_settings();
237+
if(!settings) {
238+
return 0;
239+
}
240+
241+
if(sampling_logic_exit(ctx, syscall_id, settings)) {
225242
return 0;
226243
}
227244

228-
if(maps__get_drop_failed() && ret < 0) {
245+
if(settings->drop_failed && ret < 0) {
229246
return 0;
230247
}
231248

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

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -131,6 +131,12 @@ int BPF_PROG(execve_x, struct pt_regs *regs, long ret) {
131131

132132
SEC("tp_btf/sys_exit")
133133
int BPF_PROG(t1_execve_x, struct pt_regs *regs, long ret) {
134+
/* ROX-31971: This tail call is never reached because execve_x returns
135+
* early for both success (ret == 0) and failure cases. Kept as a stub
136+
* to avoid exceeding the BPF verifier's 1M instruction limit on some
137+
* kernels (e.g. RHEL SAP 9.4).
138+
*/
139+
#if 0
134140
struct auxiliary_map *auxmap = auxmap__get();
135141
if(!auxmap) {
136142
return 0;
@@ -255,11 +261,14 @@ int BPF_PROG(t1_execve_x, struct pt_regs *regs, long ret) {
255261
/*=============================== COLLECT PARAMETERS ===========================*/
256262

257263
bpf_tail_call(ctx, &syscall_exit_extra_tail_table, T2_EXECVE_X);
264+
#endif
258265
return 0;
259266
}
260267

261268
SEC("tp_btf/sys_exit")
262269
int BPF_PROG(t2_execve_x, struct pt_regs *regs, long ret) {
270+
/* ROX-31971: unreachable, see comment in t1_execve_x. */
271+
#if 0
263272
struct auxiliary_map *auxmap = auxmap__get();
264273
if(!auxmap) {
265274
return 0;
@@ -294,6 +303,7 @@ int BPF_PROG(t2_execve_x, struct pt_regs *regs, long ret) {
294303
auxmap__finalize_event_header(auxmap);
295304

296305
auxmap__submit_event(auxmap);
306+
#endif
297307
return 0;
298308
}
299309

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

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -131,6 +131,12 @@ int BPF_PROG(execveat_x, struct pt_regs *regs, long ret) {
131131

132132
SEC("tp_btf/sys_exit")
133133
int BPF_PROG(t1_execveat_x, struct pt_regs *regs, long ret) {
134+
/* ROX-31971: This tail call contributes to exceeding the BPF verifier's
135+
* 1M instruction limit on some kernels (e.g. RHCOS 4.16, RHEL SAP 9.4).
136+
* Collector does not subscribe to execveat, so this code is not needed.
137+
* Kept as a stub so the program symbol exists in the skeleton.
138+
*/
139+
#if 0
134140
struct auxiliary_map *auxmap = auxmap__get();
135141
if(!auxmap) {
136142
return 0;
@@ -254,11 +260,14 @@ int BPF_PROG(t1_execveat_x, struct pt_regs *regs, long ret) {
254260
/*=============================== COLLECT PARAMETERS ===========================*/
255261

256262
bpf_tail_call(ctx, &syscall_exit_extra_tail_table, T2_EXECVEAT_X);
263+
#endif
257264
return 0;
258265
}
259266

260267
SEC("tp_btf/sys_exit")
261268
int BPF_PROG(t2_execveat_x, struct pt_regs *regs, long ret) {
269+
/* ROX-31971: See comment on t1_execveat_x above. */
270+
#if 0
262271
struct auxiliary_map *auxmap = auxmap__get();
263272
if(!auxmap) {
264273
return 0;
@@ -289,6 +298,7 @@ int BPF_PROG(t2_execveat_x, struct pt_regs *regs, long ret) {
289298
auxmap__finalize_event_header(auxmap);
290299

291300
auxmap__submit_event(auxmap);
301+
#endif
292302
return 0;
293303
}
294304

userspace/libpman/src/lifecycle.c

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -128,7 +128,32 @@ int pman_prepare_progs_before_loading() {
128128
progs[chosen_idx] = old_prog;
129129
}
130130

131-
// Keep autoloading enabled for all TOCTOU mitigation 64 bit programs.
131+
// Disable autoloading for TOCTOU mitigation 64-bit programs whose
132+
// syscalls don't exist on this kernel (e.g. openat2 on kernels < 5.6).
133+
// Without this, CO-RE relocations fail for missing BTF types.
134+
for(int i = 0; i < TTM_MAX; i++) {
135+
const char *prog_name = ttm_progs_table[i].ttm_64bit_prog.name;
136+
if(prog_name == NULL) {
137+
continue;
138+
}
139+
/* Check if the tracepoint exists: /sys/kernel/tracing/events/syscalls/sys_enter_<syscall>
140+
* The prog name is "<syscall>_e", so strip the "_e" suffix.
141+
*/
142+
char path[256];
143+
snprintf(path, sizeof(path),
144+
"/sys/kernel/tracing/events/syscalls/sys_enter_%.*s",
145+
(int)(strlen(prog_name) - 2), prog_name);
146+
if(access(path, F_OK) != 0) {
147+
/* Also try debugfs mount point */
148+
snprintf(path, sizeof(path),
149+
"/sys/kernel/debug/tracing/events/syscalls/sys_enter_%.*s",
150+
(int)(strlen(prog_name) - 2), prog_name);
151+
if(access(path, F_OK) != 0) {
152+
disable_prog_autoloading(msg, prog_name);
153+
}
154+
}
155+
}
156+
132157
// Disable autoloading for unsupported TOCTOU mitigation ia-32 programs.
133158
for(int i = 0; i < TTM_MAX; i++) {
134159
const ttm_ia32_prog_t *ia32_progs = ttm_progs_table[i].ttm_ia32_progs;

userspace/libsinsp/parsers.cpp

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1570,6 +1570,8 @@ void sinsp_parser::parse_execve_exit(sinsp_evt &evt, sinsp_parser_verdict &verdi
15701570
* In older event versions we can only rely on our userspace reconstruction
15711571
*/
15721572

1573+
bool exepath_set = false;
1574+
15731575
// If we are not able to retrieve the enter event we can do nothing.
15741576
sinsp_evt *enter_evt = &m_tmp_evt;
15751577
if(retrieve_enter_event(*enter_evt, evt)) {
@@ -1659,6 +1661,29 @@ void sinsp_parser::parse_execve_exit(sinsp_evt &evt, sinsp_parser_verdict &verdi
16591661
}
16601662
}
16611663
evt.get_tinfo()->set_exepath(std::move(fullpath));
1664+
exepath_set = true;
1665+
}
1666+
1667+
/* Modern drivers no longer send enter events (marked EF_OLD_VERSION),
1668+
* so the enter event reconstruction above will fail. Fall back to the
1669+
* filename parameter (bprm->filename) from the exit event, which
1670+
* contains the first argument to execve as provided by the caller.
1671+
*/
1672+
if(!exepath_set) {
1673+
/* Parameter 31: filename (type: PT_FSPATH) */
1674+
if(const auto filename_param = evt.get_param(30); !filename_param->empty()) {
1675+
std::string_view filename = filename_param->as<std::string_view>();
1676+
/* Skip fd-based execs (fexecve / container runtime re-exec).
1677+
* These are intermediate exec steps; the real exec follows.
1678+
*/
1679+
if(filename != "<NA>" &&
1680+
filename.substr(0, 8) != "/dev/fd/" &&
1681+
filename.substr(0, 15) != "/proc/self/fd/") {
1682+
std::string fullpath = sinsp_utils::concatenate_paths(
1683+
evt.get_tinfo()->get_cwd(), filename);
1684+
evt.get_tinfo()->set_exepath(std::move(fullpath));
1685+
}
1686+
}
16621687
}
16631688
}
16641689

@@ -4271,7 +4296,7 @@ void sinsp_parser::parse_pidfd_getfd_exit(sinsp_evt &evt) const {
42714296
pidfd = evt.get_param(1)->as<int64_t>();
42724297

42734298
/* targetfd */
4274-
ASSERT(evt.get_param(2)->m_len == sizeof(int64_t));
4299+
ASSERT(evt.get_param(2)->len() == sizeof(int64_t));
42754300
ASSERT(evt.get_param_info(2)->type == PT_FD);
42764301
targetfd = evt.get_param(2)->as<int64_t>();
42774302

userspace/libsinsp/sinsp_public.h

Lines changed: 28 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,12 @@ limitations under the License.
2222
#ifndef ASSERT
2323

2424
#include <assert.h>
25+
#include <stdio.h>
26+
27+
#include <libscap/scap_log.h>
28+
29+
// We expect the global logger_fn be provided from the outside
30+
extern falcosecurity_log_fn logger_fn;
2531

2632
#ifdef _DEBUG
2733

@@ -30,34 +36,36 @@ limitations under the License.
3036
#endif
3137

3238
#ifdef ASSERT_TO_LOG
33-
#define ASSERT(X) \
34-
do { \
35-
if(!(X)) { \
36-
libsinsp_logger()->format(sinsp_logger::SEV_DEBUG, \
37-
"ASSERTION %s at %s:%d", \
38-
#X, \
39-
__FILE__, \
40-
__LINE__); \
41-
} \
39+
#define ASSERT(X) \
40+
do { \
41+
if(!(X)) { \
42+
if(logger_fn != NULL) { \
43+
char buf[512]; \
44+
snprintf(buf, sizeof(buf), "ASSERTION " #X " at %s:%d", __FILE__, __LINE__); \
45+
logger_fn("libsinsp", buf, FALCOSECURITY_LOG_SEV_DEBUG); \
46+
} else { \
47+
assert(X); \
48+
} \
49+
} \
4250
} while(0)
4351
#else // ASSERT_TO_LOG
44-
#define ASSERT(X) assert(X);
52+
#define ASSERT(X) assert(X)
4553
#endif // ASSERT_TO_LOG
4654

4755
#else // _DEBUG
4856

4957
#ifdef ASSERT_TO_LOG
50-
#define ASSERT(X) \
51-
do { \
52-
if(!(X)) { \
53-
libsinsp_logger()->format(sinsp_logger::SEV_DEBUG, \
54-
"ASSERTION %s at %s:%d", \
55-
#X, \
56-
__FILE__, \
57-
__LINE__); \
58-
} \
58+
#define ASSERT(X) \
59+
do { \
60+
if(!(X)) { \
61+
if(logger_fn != NULL) { \
62+
char buf[512]; \
63+
snprintf(buf, sizeof(buf), "ASSERTION " #X " at %s:%d", __FILE__, __LINE__); \
64+
logger_fn("libsinsp", buf, FALCOSECURITY_LOG_SEV_DEBUG); \
65+
} \
66+
} \
5967
} while(0)
60-
#else
68+
#else // ASSERT_TO_LOG
6169
#define ASSERT(X)
6270
#endif // ASSERT_TO_LOG
6371

0 commit comments

Comments
 (0)