4.5 KiB
CVE-2021-47128
Description
In the Linux kernel, the following vulnerability has been resolved:bpf, lockdown, audit: Fix buggy SELinux lockdown permission checksCommit 59438b46471a ("security,lockdown,selinux: implement SELinux lockdown")added an implementation of the locked_down LSM hook to SELinux, with the aimto restrict which domains are allowed to perform operations that would breachlockdown. This is indirectly also getting audit subsystem involved to reportevents. The latter is problematic, as reported by Ondrej and Serhei, since itcan bring down the whole system via audit: 1) The audit events that are triggered due to calls to security_locked_down() can OOM kill a machine, see below details [0]. 2) It also seems to be causing a deadlock via avc_has_perm()/slow_avc_audit() when trying to wake up kauditd, for example, when using trace_sched_switch() tracepoint, see details in [1]. Triggering this was not via some hypothetical corner case, but with existing tools like runqlat & runqslower from bcc, for example, which make use of this tracepoint. Rough call sequence goes like: rq_lock(rq) -> -------------------------+ trace_sched_switch() -> | bpf_prog_xyz() -> +-> deadlock selinux_lockdown() -> | audit_log_end() -> | wake_up_interruptible() -> | try_to_wake_up() -> | rq_lock(rq) --------------+What's worse is that the intention of 59438b46471a to further restrict lockdownsettings for specific applications in respect to the global lockdown policy iscompletely broken for BPF. The SELinux policy rule for the current lockdown checklooks something like this: allow : lockdown { };However, this doesn't match with the 'current' task where the security_locked_down()is executed, example: httpd does a syscall. There is a tracing program attachedto the syscall which triggers a BPF program to run, which ends up doing abpf_probe_read_kernel{,_str}() helper call. The selinux_lockdown() hook doesthe permission check against 'current', that is, httpd in this example. httpdhas literally zero relation to this tracing program, and it would be nonsensicalhaving to write an SELinux policy rule against httpd to let the tracing helperpass. The policy in this case needs to be against the entity that is installingthe BPF program. For example, if bpftrace would generate a histogram of syscallcounts by user space application: bpftrace -e 'tracepoint:raw_syscalls:sys_enter { @[comm] = count(); }'bpftrace would then go and generate a BPF program from this internally. One wayof doing it [for the sake of the example] could be to call bpf_get_current_task()helper and then access current->comm via one of bpf_probe_read_kernel{,_str}()helpers. So the program itself has nothing to do with httpd or any other randomapp doing a syscall here. The BPF program explicitly initiated the lockdowncheck. The allow/deny policy belongs in the context of bpftrace: meaning, youwant to grant bpftrace access to use these helpers, but other tracers on thesystem like my_random_tracer not.Therefore fix all three issues at the same time by taking a completely differentapproach for the security_locked_down() hook, that is, move the check into theprogram verification phase where we actually retrieve the BPF func proto. Thisalso reliably gets the task (current) that is trying to install the BPF tracingprogram, e.g. bpftrace/bcc/perf/systemtap/etc, and it also fixes the OOM sincewe're moving this out of the BPF helper's fast-path which can be called severalmillions of times per second.The check is then also in line with other security_locked_down() hooks in thesystem where the enforcement is performed at open/load time, for example,open_kcore() for /proc/kcore access or module_sig_check() for module signaturesjust to pick f---truncated---
POC
Reference
No PoCs from references.