1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
//! Pure Linux credential policy for task-targeted perf events.
/// Credential fields consumed by `PTRACE_MODE_READ_REALCREDS`.
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub(crate) struct PerfCredentialIds {
uid: u32,
gid: u32,
euid: u32,
egid: u32,
suid: u32,
sgid: u32,
}
impl PerfCredentialIds {
/// Creates one immutable real/effective/saved id snapshot.
pub(crate) const fn new(
uid: u32,
gid: u32,
euid: u32,
egid: u32,
suid: u32,
sgid: u32,
) -> Self {
Self {
uid,
gid,
euid,
egid,
suid,
sgid,
}
}
}
/// Capabilities that bypass the ptrace-style id checks.
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub(crate) struct PerfAccessCapabilities {
perfmon_capable: bool,
ptrace_capable: bool,
kill_capable: bool,
}
impl PerfAccessCapabilities {
/// Creates one capability snapshot.
pub(crate) const fn new(
perfmon_capable: bool,
ptrace_capable: bool,
kill_capable: bool,
) -> Self {
Self {
perfmon_capable,
ptrace_capable,
kill_capable,
}
}
}
/// Complete credential snapshot consumed by one perf access check.
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub(crate) struct PerfCredentialSnapshot {
ids: PerfCredentialIds,
capabilities: PerfAccessCapabilities,
}
impl PerfCredentialSnapshot {
/// Creates one immutable permission-check snapshot.
pub(crate) const fn new(ids: PerfCredentialIds, capabilities: PerfAccessCapabilities) -> Self {
Self { ids, capabilities }
}
}
/// Applies Linux v7.1 `perf_check_permission()` task-access semantics.
///
/// Same-thread-group targets and callers with `CAP_PERFMON` (or its
/// `CAP_SYS_ADMIN` compatibility fallback) bypass the ptrace-style check.
/// A `sigtrap` event also requires `CAP_KILL` for that capability bypass,
/// because the event can inject a signal into the target. Otherwise
/// `PTRACE_MODE_READ_REALCREDS` (or its attach variant for `sigtrap`) requires
/// the caller's real uid/gid to match every target uid/gid slot, and the
/// target must remain dumpable. `CAP_SYS_PTRACE` bypasses both the id and
/// dumpability restrictions.
pub(crate) const fn perf_task_access_allowed(
caller: PerfCredentialSnapshot,
target: PerfCredentialSnapshot,
same_thread_group: bool,
target_dumpable: bool,
signal_delivery: bool,
) -> bool {
let perfmon_bypass = caller.capabilities.perfmon_capable
&& (!signal_delivery || caller.capabilities.kill_capable);
if same_thread_group || perfmon_bypass || caller.capabilities.ptrace_capable {
return true;
}
target_dumpable
&& caller.ids.uid == target.ids.uid
&& caller.ids.uid == target.ids.euid
&& caller.ids.uid == target.ids.suid
&& caller.ids.gid == target.ids.gid
&& caller.ids.gid == target.ids.egid
&& caller.ids.gid == target.ids.sgid
}