CVE-2026-89761
High · 7.8CVSS
7.8
EPSS
0.1%
Published
Sep 11, 2026
CISA due date
—
Cloudanix analysis
A bug in the Linux security module that decides which programs are allowed to do what lets an ordinary user program overwrite important internal memory and potentially take over the machine.
- Exploitation
- Local Privilege
- Attacker needs
- Low Privilege
- Complexity
- Low
- Impact
- Full Compromise
- Component
- OS kernel
- Class
- Buffer Overflow
- Remediation
- Patch
- Effort
- Medium
- Patch risk
- Low
What it actually does
Out-of-bounds write caused by aa_vec_unique() writing a null terminator one entry past the end of a locally allocated or kzalloc'd vector when aa_label_strn_parse() supplies a label with exactly LOCAL_VEC_ENTRIES or more "//&" components; vec_setup() and DEFINE_VEC() allocate exactly 'len' pointers without the required +1 terminator slot.
Why it matters to the business
Local attackers on Linux systems running AppArmor can corrupt kernel memory, potentially leading to privilege escalation, container escape, or denial of service; widespread impact on servers and desktops that enforce AppArmor profiles.
How an attack unfolds
An unprivileged process writes a crafted label containing many distinct components to /proc/self/attr/apparmor/current (or calls lsm_set_self_attr) or queries via the world-writable securityfs .access file; label_count_strn_entries() supplies an unbounded len, vec_setup() allocates insufficient space, aa_vec_unique() with VEC_FLAG_TERMINATE performs the out-of-bounds store, and the kernel may later dereference the corrupted vector.
How to fix it
Apply the upstream kernel patch that updates vec_setup() and DEFINE_VEC() to reserve space for the terminator entry; rebuild and deploy fixed kernels; restrict write access to /proc/self/attr/apparmor/current and securityfs .access where possible until patched.
How to confirm the fix worked
Confirm kernel source contains the corrected vec_setup() allocation of len+1 entries; run regression tests exercising aa_label_strn_parse() with labels of length LOCAL_VEC_ENTRIES and larger; monitor for crashes or KASAN reports on label parse paths.
Typical attack path
- Identify AppArmor-enabled target with loaded policy
- Craft label name with LOCAL_VEC_ENTRIES or more distinct components separated by //&
- Write label to /proc/self/attr/apparmor/current or invoke lsm_set_self_attr(2) before permission check
- Trigger aa_label_strn_parse() -> vec_setup() -> aa_vec_unique() to perform out-of-bounds write
- Exploit resulting memory corruption for privilege escalation or DoS
How to detect it
Search kernel logs for AppArmor label parse warnings or KASAN out-of-bounds writes near aa_vec_unique/aa_label_strn_parse; correlate process writes to /proc/self/attr/apparmor/current or securityfs .access file with subsequent crashes or unexpected profile changes.
- /proc/self/attr/apparmor/current
- lsm_set_self_attr
- aa_vec_unique
- aa_label_strn_parse
- VEC_FLAG_TERMINATE
What your security team should do
- Prioritize kernel updates on all AppArmor-enabled systems within 30 days
- Harden LSM attribute interfaces with additional LSM hooks or seccomp filters
- Audit workloads that rely on dynamic profile transitions for least-privilege label usage
- Enable KASAN or similar kernel sanitizers in test environments to catch similar vector sizing bugs
The record
In the Linux kernel, the following vulnerability has been resolved: apparmor: fix out-of-bounds write when null terminating a label vec aa_vec_unique() null terminates at vec[n - dups] when VEC_FLAG_TERMINATE is passed. If the components are all distinct no duplicates are dropped, dups is 0 and the terminator goes to vec[n], so the caller has to provide room for n + 1 entries. aa_label_strn_parse() sets up its vector with vec_setup(profile, vec, len, gfp) and then calls aa_vec_unique(vec, len, VEC_FLAG_TERMINATE), but vec_setup() does not reserve the terminator entry. Up to LOCAL_VEC_ENTRIES it uses the local array of LOCAL_VEC_ENTRIES pointers, above that it allocates exactly len pointers. The terminator therefore lands one entry past the end of the local array when len is LOCAL_VEC_ENTRIES, and one entry past the end of the allocation when len is larger. len comes from the number of "//&" separated components in the label name and label_count_strn_entries() does not bound it. An unprivileged task reaches the parse by writing to /proc/self/attr/apparmor/current or through lsm_set_self_attr(2), both of which go through do_setattr(), and the name is parsed before the change_profile permission is checked. The query_label() path behind the securityfs .access file, which is mode 0666, performs no permission check at all. Every component has to resolve to a loaded profile, so a system with policy loaded is required. The other two VEC_FLAG_TERMINATE users work on a label vec that aa_label_alloc() has already sized with "+ 1 for null terminator entry on vec". Reserve the same entry in vec_setup() and DEFINE_VEC(). Passing len + 1 from the caller instead would move len == LOCAL_VEC_ENTRIES out of the local array and into kzalloc().
Affected packages
| Ecosystem | Package | Introduced | Fixed |
|---|