{"id":21684,"date":"2026-09-12T04:31:31","date_gmt":"2026-09-11T20:31:31","guid":{"rendered":"https:\/\/www.54iter.cn\/?vuln=cve-2026-89762"},"modified":"2026-09-12T04:31:31","modified_gmt":"2026-09-11T20:31:31","slug":"cve-2026-89762","status":"publish","type":"vuln","link":"https:\/\/www.54iter.cn\/?vuln=cve-2026-89762","title":{"rendered":"CVE-2026-89762 &#8211; In the Linux kernel, the following vulnerability has been resolved:\n\napparmor: fix cred UA"},"content":{"rendered":"<p>In the Linux kernel, the following vulnerability has been resolved:<\/p>\n<p>apparmor: fix cred UAF caused by begin_current_label_crit_section()<\/p>\n<p>AppArmor&#8217;s begin_current_label_crit_section() is a scary function called<br \/>\nfrom lots of LSM hooks (in particular VFS\/socket-related ones) that checks<br \/>\nif the label referenced by the current creds is marked FLAG_STALE, and if<br \/>\nso, attempts to use aa_replace_current_label() to replace the creds with an<br \/>\nupdated version that uses a new label.<\/p>\n<p>The first problem with this is that it would directly lead to UAF of<br \/>\n`struct cred` if anything in the kernel takes a pointer to the current<br \/>\ncreds and accesses these past a security hook invocation that replaces<br \/>\ncreds, like so:<br \/>\n&#8220;`<br \/>\nconst struct cred *cred = current_cred();<br \/>\nalloc_file_pseudo(&#8230;);<br \/>\nuid_t uid = cred-&gt;euid;<br \/>\n&#8220;`<br \/>\nI don&#8217;t know if anything in the kernel actually does this, but I think it<br \/>\nis very surprising that this pattern could lead to UAF.<\/p>\n<p>The second problem is that things go wrong when aa_replace_current_label()<br \/>\nruns with overridden credentials. aa_replace_current_label() bails out if<br \/>\n`current_cred() != current_real_cred()` (mirroring the check in<br \/>\nproc_pid_attr_write()), but this check can&#8217;t actually reliably detect<br \/>\noverridden credentials because the overridden creds can be the same as the<br \/>\nobjective creds.<\/p>\n<p>So in approximately the following scenario, things go wrong:<\/p>\n<p>1. task begins with  (as both objective and subjective creds),<br \/>\n   with refcount=2<br \/>\n2. task grabs an extra reference on  for overriding<br \/>\n3. task calls override_creds(), which returns a pointer to the old<br \/>\n   subjective creds ()<br \/>\n4. task enters AppArmor LSM hook<br \/>\n5. AppArmor checks that objective\/subjective creds are equal<br \/>\n6. AppArmor replaces both cred pointers with  and drops 2 refs on<\/p>\n<p>7. task leaves AppArmor LSM hook<br \/>\n8. task calls revert_creds()<br \/>\n9. now task-&gt;cred is  while task-&gt;real_cred is , but the<br \/>\n   task_struct logically holds two references to<br \/>\n10. another task drops the extra reference on  that was used for<br \/>\n    overriding, refcount drops to 0<br \/>\n11. now task-&gt;real_cred points to freed creds<\/p>\n<p>At this point, any access to current_cred() will be UAF.<\/p>\n<p>I have a test case where I run aa-disable on a profile while a process<br \/>\nusing that profile is blocked on splice() from a FUSE passthrough file into<br \/>\na full pipe; after the profile update, the pipe becomes empty, splice()<br \/>\nresumes, the credentials go out of sync, and a subsequent getuid() syscall<br \/>\nresults in a KASAN UAF splat.<\/p>\n<p>To fix this, instead of directly replacing creds, do it via task_work that<br \/>\nwill run at the end of the current syscall. (The point in time at which the<br \/>\ncred replacement happens should have no correctness impact; it is just a<br \/>\nperformance optimization to avoid unnecessarily touching the refcount of<br \/>\nthe new label.)<\/p>\n<p>Note that AppArmor still performs direct cred replacements in the<br \/>\nsb_pivotroot LSM hook after this change, and that direct cred replacements<br \/>\ncan still happen in VFS -&gt;write() callbacks via proc_pid_attr_write().<\/p>\n<p>There are two options for what to do with aa_dup_task_ctx(): Either<br \/>\nexplicitly reset new-&gt;label_replacement_pending after the entire<br \/>\naa_task_ctx has been copied, or switch to manually copying members over.<br \/>\nI am switching to manually copying members over because that should make<br \/>\nbugs more obvious.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>In the Linux kernel, the following vulnerability has been resolved:<\/p>\n<p>apparmor: fix cred UAF caused by begin_current_label_crit_section()<\/p>\n<p>AppArmor&#8217;s begin_current_label_crit_section() is a scary funct<\/p>\n","protected":false},"template":"","meta":[],"categories":[],"tags":[],"class_list":["post-21684","vuln","type-vuln","status-publish","hentry"],"_links":{"self":[{"href":"https:\/\/www.54iter.cn\/index.php?rest_route=\/wp\/v2\/vuln\/21684","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.54iter.cn\/index.php?rest_route=\/wp\/v2\/vuln"}],"about":[{"href":"https:\/\/www.54iter.cn\/index.php?rest_route=\/wp\/v2\/types\/vuln"}],"wp:attachment":[{"href":"https:\/\/www.54iter.cn\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=21684"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.54iter.cn\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=21684"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.54iter.cn\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=21684"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}