CVE-2026-90246: apparmor: fix integer overflow in verify_tags() bounds check

Published Sep 17, 2026
·
Updated

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

apparmor: fix integer overflow in verifytags() bounds check

verifytags() validates the tagset table unpacked from a policy blob. For each set it reads a count and checks that advancing the index by that count stays inside sets.table[]:

u32 cnt = tags->sets.table[i];

if (i+cnt >= tags->sets.size) {

i, cnt and sets.size are all u32, so i+cnt is evaluated modulo 2^32. sets.table[] is filled by unpacktagsets() with aaunpacku32(), so every entry is a raw unbounded 32-bit word taken from the policy blob, and verifytags() is the function that is supposed to validate it. A count close to U32MAX makes the sum wrap to a small value, the guard passes, and the inner loop then walks sets.table[++i] past the end of the kcalloc(size, sizeof(u32)) allocation.

Note that sets.size is bounded by 65535, because unpacktagsets() reads it with aaunpackarray() as a u16, so the wrap cannot be reached by growing the table; it is reached purely through the attacker-supplied count.

With sets.size = 2 and sets.table = { 0, 0xffffffff }:

i = 0: cnt = 0, guard 0 + 0 >= 2 is false, inner loop does not run i = 1: cnt = 0xffffffff, guard (1 + 0xffffffff) mod 2^32 == 0 >= 2 is false, so the guard is bypassed and the inner loop reads sets.table[2] -- one element past a two element allocation

The walk continues until an out-of-bounds value happens to be >= hdrs.size or the access faults, so a crafted policy yields an out-of-bounds read on the policy load path (aareplaceprofiles -> aaunpack -> unpackpolicydb -> unpacktags -> verifytags). unpacktags() runs before the perms and DFA tables are unpacked, so no other table needs to be well formed to reach it.

Policy load is gated by aamaymanagepolicy(), which checks CAPMACADMIN relative to the subject's own user namespace rather than the init user namespace, so with the default unprivilegedusernsapparmorpolicy=1 the path is reachable from an unprivileged task in a matched-level nested namespace, not only by a globally privileged one.

Perform the addition in u64 so that it cannot wrap, restoring the intended i + cnt < sets.size guarantee.

Affected Software

1 affected component
Linux Linux kernel

Event History

Sep 17, 2026
CVE Published
via MITRE·04:07 PM
Data Sourced
via MITRE·04:07 PM
Description

Frequently Asked Questions

1

What must an attacker control to trigger this issue?

The attacker needs to supply a policy blob containing a crafted tagset table. In particular, a tag count near U32_MAX can cause the 32-bit bounds-check addition to wrap.

2

Which systems are exposed?

Systems are exposed when the kernel processes an attacker-controlled AppArmor policy blob with tagsets. The issue is in validation of the unpacked tagset table.

3

Does the tagset table size limit prevent exploitation?

No. Although the table size is bounded to 65535 entries, the individual count values are raw, unbounded 32-bit values from the policy blob, and exploitation relies on the count rather than growing the table.

4

What happens after the bounds check is bypassed?

The inner loop can advance beyond the end of the kcalloc-allocated sets.table array. This results from the wrapped addition allowing an invalid count to pass validation.

Contact

SecAlerts Pty Ltd.
132 Wickham Terrace
Fortitude Valley,
QLD 4006, Australia
info@secalerts.co
By using SecAlerts services, you agree to our services end-user license agreement. This website is safeguarded by reCAPTCHA and governed by the Google Privacy Policy and Terms of Service. All names, logos, and brands of products are owned by their respective owners, and any usage of these names, logos, and brands for identification purposes only does not imply endorsement. If you possess any content that requires removal, please get in touch with us.
© 2026 SecAlerts Pty Ltd.
ABN: 70 645 966 203, ACN: 645 966 203