CVE-2021-47128

Source
https://nvd.nist.gov/vuln/detail/CVE-2021-47128
Import Source
https://storage.googleapis.com/cve-osv-conversion/osv-output/CVE-2021-47128.json
JSON Data
https://api.osv.dev/v1/vulns/CVE-2021-47128
Related
Published
2024-03-15T21:15:07Z
Modified
2025-03-13T21:24:38Z
Severity
  • 5.5 (Medium) CVSS_V3 - CVSS:3.1/AV:L/AC:L/PR:L/UI:N/S:U/C:N/I:N/A:H CVSS Calculator
Summary
[none]
Details

In the Linux kernel, the following vulnerability has been resolved:

bpf, lockdown, audit: Fix buggy SELinux lockdown permission checks

Commit 59438b46471a ("security,lockdown,selinux: implement SELinux lockdown") added an implementation of the locked_down LSM hook to SELinux, with the aim to restrict which domains are allowed to perform operations that would breach lockdown. This is indirectly also getting audit subsystem involved to report events. The latter is problematic, as reported by Ondrej and Serhei, since it can bring down the whole system via audit:

1) The audit events that are triggered due to calls to securitylockeddown() can OOM kill a machine, see below details [0].

2) It also seems to be causing a deadlock via avchasperm()/slowavcaudit() when trying to wake up kauditd, for example, when using traceschedswitch() 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 lockdown settings for specific applications in respect to the global lockdown policy is completely broken for BPF. The SELinux policy rule for the current lockdown check looks something like this:

allow <who> <who> : lockdown { <reason> };

However, this doesn't match with the 'current' task where the securitylockeddown() is executed, example: httpd does a syscall. There is a tracing program attached to the syscall which triggers a BPF program to run, which ends up doing a bpfprobereadkernel{,str}() helper call. The selinux_lockdown() hook does the permission check against 'current', that is, httpd in this example. httpd has literally zero relation to this tracing program, and it would be nonsensical having to write an SELinux policy rule against httpd to let the tracing helper pass. The policy in this case needs to be against the entity that is installing the BPF program. For example, if bpftrace would generate a histogram of syscall counts by user space application:

bpftrace -e 'tracepoint:rawsyscalls:sysenter { @[comm] = count(); }'

bpftrace would then go and generate a BPF program from this internally. One way of doing it [for the sake of the example] could be to call bpfgetcurrenttask() helper and then access current->comm via one of bpfprobereadkernel{,str}() helpers. So the program itself has nothing to do with httpd or any other random app doing a syscall here. The BPF program _explicitly initiated the lockdown check. The allow/deny policy belongs in the context of bpftrace: meaning, you want to grant bpftrace access to use these helpers, but other tracers on the system like myrandomtracer not.

Therefore fix all three issues at the same time by taking a completely different approach for the securitylockeddown() hook, that is, move the check into the program verification phase where we actually retrieve the BPF func proto. This also reliably gets the task (current) that is trying to install the BPF tracing program, e.g. bpftrace/bcc/perf/systemtap/etc, and it also fixes the OOM since we're moving this out of the BPF helper's fast-path which can be called several millions of times per second.

The check is then also in line with other securitylockeddown() hooks in the system where the enforcement is performed at open/load time, for example, openkcore() for /proc/kcore access or modulesig_check() for module signatures just to pick f ---truncated---

References

Affected packages

Debian:11 / linux

Package

Name
linux
Purl
pkg:deb/debian/linux?arch=source

Affected ranges

Type
ECOSYSTEM
Events
Introduced
0Unknown introduced version / All previous versions are affected
Fixed
5.10.46-1

Ecosystem specific

{
    "urgency": "not yet assigned"
}

Debian:12 / linux

Package

Name
linux
Purl
pkg:deb/debian/linux?arch=source

Affected ranges

Type
ECOSYSTEM
Events
Introduced
0Unknown introduced version / All previous versions are affected
Fixed
5.10.46-1

Ecosystem specific

{
    "urgency": "not yet assigned"
}

Debian:13 / linux

Package

Name
linux
Purl
pkg:deb/debian/linux?arch=source

Affected ranges

Type
ECOSYSTEM
Events
Introduced
0Unknown introduced version / All previous versions are affected
Fixed
5.10.46-1

Ecosystem specific

{
    "urgency": "not yet assigned"
}