Skip to content

Commit 03f984c

Browse files
Setrlimit event capture (#37561)
Co-authored-by: spikat <jonathan.ribas@datadoghq.com>
1 parent cf48b87 commit 03f984c

29 files changed

+20427
-9037
lines changed

docs/cloud-workload-security/linux_expressions.md

Lines changed: 429 additions & 124 deletions
Large diffs are not rendered by default.

docs/cloud-workload-security/secl_linux.json

Lines changed: 1621 additions & 2 deletions
Large diffs are not rendered by default.

pkg/security/ebpf/c/include/constants/enums.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,7 @@ enum event_type
5858
EVENT_NETWORK_FLOW_MONITOR,
5959
EVENT_STAT,
6060
EVENT_SYSCTL,
61+
EVENT_SETRLIMIT,
6162
EVENT_SETSOCKOPT,
6263
EVENT_MAX, // has to be the last one
6364

pkg/security/ebpf/c/include/events_definition.h

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -518,6 +518,19 @@ struct sysctl_event_t {
518518
char sysctl_buffer[MAX_SYSCTL_BUFFER_LEN];
519519
};
520520

521+
struct setrlimit_event_t {
522+
struct kevent_t event;
523+
struct process_context_t process;
524+
struct span_context_t span;
525+
struct container_context_t container;
526+
struct syscall_t syscall;
527+
528+
int resource;
529+
u32 target;
530+
u64 rlim_cur;
531+
u64 rlim_max;
532+
};
533+
521534
struct setsockopt_event_t {
522535
struct kevent_t event;
523536
struct process_context_t process;

pkg/security/ebpf/c/include/hooks/all.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@
3636
#include "utimes.h"
3737
#include "on_demand.h"
3838
#include "chdir.h"
39+
#include "setrlimit.h"
3940

4041
#include "network/accept.h"
4142
#include "network/bind.h"
Lines changed: 145 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,145 @@
1+
#ifndef _HOOKS_SETRLIMIT_H_
2+
#define _HOOKS_SETRLIMIT_H_
3+
4+
#include "constants/syscall_macro.h"
5+
#include "helpers/discarders.h"
6+
#include "helpers/syscalls.h"
7+
#include "events_definition.h"
8+
9+
#define SETRLIMIT_RATE_LIMITER 100
10+
11+
static const int important_resources[] = {
12+
RLIMIT_CPU,
13+
RLIMIT_FSIZE,
14+
RLIMIT_NOFILE,
15+
RLIMIT_STACK,
16+
RLIMIT_NPROC,
17+
RLIMIT_CORE
18+
};
19+
20+
static __always_inline int handle_setrlimit_common(unsigned int resource, const struct rlimit __user *new_rlim, u32 target_pid)
21+
{
22+
bool is_important = false;
23+
for (int i = 0; i < ARRAY_SIZE(important_resources); i++) {
24+
if (resource == important_resources[i]) {
25+
is_important = true;
26+
break;
27+
}
28+
}
29+
if (!is_important &&
30+
!pid_rate_limiter_allow(SETRLIMIT_RATE_LIMITER, 1)) {
31+
return 0;
32+
}
33+
34+
struct rlimit rlim;
35+
if (bpf_probe_read_user(&rlim, sizeof(rlim), new_rlim) < 0) {
36+
return 0;
37+
}
38+
39+
struct syscall_cache_t cache = {
40+
.type = EVENT_SETRLIMIT,
41+
.setrlimit = {
42+
.resource = resource,
43+
.pid = target_pid,
44+
.rlim_cur = rlim.rlim_cur,
45+
.rlim_max = rlim.rlim_max,
46+
}
47+
};
48+
49+
cache_syscall(&cache);
50+
return 0;
51+
}
52+
53+
HOOK_SYSCALL_ENTRY2(setrlimit,
54+
unsigned int, resource,
55+
const struct rlimit __user *, new_rlim)
56+
{
57+
return handle_setrlimit_common(resource, new_rlim, 0);
58+
}
59+
60+
HOOK_ENTRY("security_task_setrlimit")
61+
int hook_security_task_setrlimit(ctx_t *ctx)
62+
{
63+
struct task_struct *task = (struct task_struct *)CTX_PARM1(ctx);
64+
if (!task) {
65+
return 0;
66+
}
67+
68+
struct syscall_cache_t *cache = peek_syscall(EVENT_SETRLIMIT);
69+
if (!cache) {
70+
return 0;
71+
}
72+
73+
// Get the root namespace PID for the target process
74+
u32 root_pid = get_root_nr_from_task_struct(task);
75+
if (root_pid == 0) {
76+
return 0;
77+
}
78+
79+
// Update the cache with the target process information
80+
cache->setrlimit.pid = root_pid;
81+
82+
return 0;
83+
}
84+
85+
static __always_inline int
86+
sys_setrlimit_ret(void *ctx, int ret)
87+
{
88+
struct syscall_cache_t *cache = pop_syscall(EVENT_SETRLIMIT);
89+
if (!cache) {
90+
return 0;
91+
}
92+
93+
if (ret != 0 && ret != -EPERM) {
94+
return 0;
95+
}
96+
97+
if (cache->setrlimit.pid == 0) {
98+
u32 fallback = bpf_get_current_pid_tgid() >> 32;
99+
cache->setrlimit.pid = fallback;
100+
}
101+
102+
struct setrlimit_event_t evt = {
103+
.syscall.retval = ret,
104+
.resource = cache->setrlimit.resource,
105+
.rlim_cur = cache->setrlimit.rlim_cur,
106+
.rlim_max = cache->setrlimit.rlim_max,
107+
.target = cache->setrlimit.pid,
108+
};
109+
110+
struct proc_cache_t *pc = fill_process_context(&evt.process);
111+
fill_container_context(pc, &evt.container);
112+
fill_span_context(&evt.span);
113+
114+
send_event(ctx, EVENT_SETRLIMIT, evt);
115+
return 0;
116+
}
117+
118+
HOOK_SYSCALL_EXIT(setrlimit) {
119+
return sys_setrlimit_ret(ctx, (int)SYSCALL_PARMRET(ctx));
120+
}
121+
122+
TAIL_CALL_TRACEPOINT_FNC(handle_sys_setrlimit_exit,
123+
struct tracepoint_raw_syscalls_sys_exit_t *args)
124+
{
125+
return sys_setrlimit_ret(args, args->ret);
126+
}
127+
128+
HOOK_SYSCALL_ENTRY4(prlimit64,
129+
pid_t, pid,
130+
int, resource,
131+
const struct rlimit __user *, new_limit,
132+
struct rlimit __user *, old_limit)
133+
{
134+
if (new_limit == NULL) {
135+
return 0;
136+
}
137+
138+
return handle_setrlimit_common(resource, new_limit, pid);
139+
}
140+
141+
HOOK_SYSCALL_EXIT(prlimit64) {
142+
return sys_setrlimit_ret(ctx, (int)SYSCALL_PARMRET(ctx));
143+
}
144+
145+
#endif // _HOOKS_SETRLIMIT_H_

pkg/security/ebpf/c/include/structs/syscalls.h

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,15 @@ struct syscall_cache_t {
7474
struct file_t target_file;
7575
} rename;
7676

77+
struct {
78+
int resource;
79+
u64 rlim_cur;
80+
u64 rlim_max;
81+
u32 pid;
82+
struct process_context_t target_process;
83+
struct container_context_t target_container;
84+
} setrlimit;
85+
7786
struct {
7887
struct dentry *dentry;
7988
struct path *path;

pkg/security/ebpf/probes/all.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,7 @@ func AllProbes(fentry bool, cgroup2MountPoint string) []*manager.Probe {
113113
allProbes = append(allProbes, GetOnDemandProbes()...)
114114
allProbes = append(allProbes, GetPerfEventProbes()...)
115115
allProbes = append(allProbes, getSysCtlProbes(cgroup2MountPoint)...)
116+
allProbes = append(allProbes, getSetrlimitProbes(fentry)...)
116117

117118
allProbes = append(allProbes,
118119
&manager.Probe{

pkg/security/ebpf/probes/event_types.go

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -536,6 +536,13 @@ func GetSelectorsPerEventType(fentry bool) map[eval.EventType][]manager.ProbesSe
536536
hookFunc("hook_proc_sys_call_handler"),
537537
}},
538538
},
539+
"setrlimit": {
540+
&manager.BestEffort{Selectors: ExpandSyscallProbesSelector(SecurityAgentUID, "setrlimit", fentry, EntryAndExit)},
541+
&manager.BestEffort{Selectors: ExpandSyscallProbesSelector(SecurityAgentUID, "prlimit64", fentry, EntryAndExit)},
542+
&manager.AllOf{Selectors: []manager.ProbesSelector{
543+
hookFunc("hook_security_task_setrlimit"),
544+
}},
545+
},
539546
}
540547

541548
// Add probes required to track network interfaces and map network flows to processes

pkg/security/ebpf/probes/raw_sys_exit.go

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -205,6 +205,13 @@ func getSysExitTailCallRoutes() []manager.TailCallRoute {
205205
EBPFFuncName: tailCallTracepointFnc("handle_sys_newfstatat_exit"),
206206
},
207207
},
208+
{
209+
ProgArrayName: "sys_exit_progs",
210+
Key: uint32(model.SetrlimitEventType),
211+
ProbeIdentificationPair: manager.ProbeIdentificationPair{
212+
EBPFFuncName: tailCallTracepointFnc("handle_sys_setrlimit_exit"),
213+
},
214+
},
208215
{
209216
ProgArrayName: "sys_exit_progs",
210217
Key: uint32(model.SetSockOptEventType),

pkg/security/ebpf/probes/setrlimit.go

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
// Unless explicitly stated otherwise all files in this repository are licensed
2+
// under the Apache License Version 2.0.
3+
// This product includes software developed at Datadog (https://www.datadoghq.com/).
4+
// Copyright 2016-present Datadog, Inc.
5+
6+
//go:build linux
7+
8+
// Package probes holds probes related files
9+
package probes
10+
11+
import manager "github.com/DataDog/ebpf-manager"
12+
13+
func getSetrlimitProbes(fentry bool) []*manager.Probe {
14+
var setrlimitProbes []*manager.Probe
15+
setrlimitProbes = append(setrlimitProbes, ExpandSyscallProbes(&manager.Probe{
16+
ProbeIdentificationPair: manager.ProbeIdentificationPair{
17+
UID: SecurityAgentUID,
18+
},
19+
SyscallFuncName: "setrlimit",
20+
}, fentry, EntryAndExit)...)
21+
setrlimitProbes = append(setrlimitProbes, ExpandSyscallProbes(&manager.Probe{
22+
ProbeIdentificationPair: manager.ProbeIdentificationPair{
23+
UID: SecurityAgentUID,
24+
},
25+
SyscallFuncName: "prlimit64",
26+
}, fentry, EntryAndExit)...)
27+
28+
// Add the LSM hook for setrlimit
29+
setrlimitProbes = append(setrlimitProbes, &manager.Probe{
30+
ProbeIdentificationPair: manager.ProbeIdentificationPair{
31+
UID: SecurityAgentUID,
32+
EBPFFuncName: "hook_security_task_setrlimit",
33+
},
34+
})
35+
36+
return setrlimitProbes
37+
}

pkg/security/probe/probe_ebpf.go

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1441,6 +1441,21 @@ func (p *EBPFProbe) handleEvent(CPU int, data []byte) {
14411441
seclog.Errorf("failed to decode setsockopt event: %s (offset %d, len %d)", err, offset, len(data))
14421442
return
14431443
}
1444+
case model.SetrlimitEventType:
1445+
if _, err = event.Setrlimit.UnmarshalBinary(data[offset:]); err != nil {
1446+
seclog.Errorf("failed to decode setrlimit event: %s (offset %d, len %d)", err, offset, len(data))
1447+
return
1448+
}
1449+
// resolve target process context
1450+
var pce *model.ProcessCacheEntry
1451+
if event.Setrlimit.TargetPid > 0 {
1452+
pce = p.Resolvers.ProcessResolver.Resolve(event.Setrlimit.TargetPid, event.Setrlimit.TargetPid, 0, false, newEntryCb)
1453+
}
1454+
if pce == nil {
1455+
pce = model.NewPlaceholderProcessCacheEntry(event.Setrlimit.TargetPid, event.Setrlimit.TargetPid, false)
1456+
}
1457+
event.Setrlimit.Target = &pce.ProcessContext
1458+
14441459
}
14451460

14461461
// resolve the container context

0 commit comments

Comments
 (0)