From nobody Thu Oct 1 15:54:24 2026 Received: from mail-wm1-f46.google.com (mail-wm1-f46.google.com [209.85.128.46]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 6FC314D2ED5 for ; Thu, 6 Aug 2026 20:16:13 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.128.46 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1786047388; cv=none; b=FPPkxCgIagbtQ8OW9NR1l7S2k45Gis3XBY3JD4nEAu0jULo9K506T0Vm93es5AqHKVgsAQhAJzRWLxX7w/qjMBIAljqnZJktVQlh8Ot6sLGJeJq+noL0lENaohcOMQpaxPqRN4iwyAeJZa3jFXpPANDuvCiH4vbMOtSJqKn0MEw= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1786047388; c=relaxed/simple; bh=iO3qoa9nWB6AZNwYVsc67efJhEGt5zYcjAApR0Or+DQ=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:To:Cc; b=IEuEBGJnAbA0w/w5Vyexde9RELoG3DIz1qN/285L25GplrNjMX8O5j7NoviDpWI/NwKvo2/dAj265bPt+X/oTSoodc9i2dv7zx84YJnn4aML2/Aee8SmibbZTOaypJokGRTbcYFKUzUdzJPxrcWZ6Qc/jivCyQvFnfmEHC0TzYU= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=google.com; spf=pass smtp.mailfrom=google.com; dkim=pass (2048-bit key) header.d=google.com header.i=@google.com header.b=FF+lkBZb; arc=none smtp.client-ip=209.85.128.46 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=google.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=google.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=google.com header.i=@google.com header.b="FF+lkBZb" Received: by mail-wm1-f46.google.com with SMTP id 5b1f17b1804b1-495509b08ebso1165e9.1 for ; Thu, 06 Aug 2026 13:16:12 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20251104; t=1786047366; x=1786652166; darn=vger.kernel.org; h=cc:to:message-id:content-transfer-encoding:content-type :mime-version:subject:date:from:from:to:cc:subject:date:message-id :reply-to:content-type; bh=p2xgAL4A/uZNv8wN6KWA5a6qdPc39oZTmGCx5UXjDpo=; b=FF+lkBZbOP+S1pTX5Kz/5brx3NniubS2GwiFrhjkwJTJ9BBVbXhCOxa9WhiPGDz6mr AvMWePBwWVbtdy1vZ+LUtmO0sH3aZ5ueKytxSsLT8t0+w32mJJBlqoG9kPuAjWd5XHG4 tISyxTCcb0ZGaNvoJfj2Aoa4Np2m2wpquwbJwjvs7FXCG1PwrsS7uc+/rMS9C45O7mw/ jlnU6VqaguPcSIRci74xU84xYgCwHoJSg/BExa0onWcFEAvber5uQRT+AUy/e6aA3dim ZbiaEieeCdlnd8kWZJpZJ/oiPi4Hpp7yducQxHOXykR69eCEcVPy+p2a3oIWN7Ueih5z rcrg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1786047366; x=1786652166; h=cc:to:message-id:content-transfer-encoding:content-type :mime-version:subject:date:from:x-gm-gg:x-gm-message-state:from:to :cc:subject:date:message-id:reply-to:content-type; bh=p2xgAL4A/uZNv8wN6KWA5a6qdPc39oZTmGCx5UXjDpo=; b=qIQLF83S2cLSfbxvye0JtMTMjf0qZapDzHyQJTAXxb/Br5qVMKgCmb4+NqcGs2am1n 6hCu21wbAxyMcYFyAMv/YfQ/ExcxscqJ8eM3u1m5vWCpcJFin5v9DEuyJ5EhZRkh39FL 8fSufosbZ9CtoEBuemj1g/j5cEUffdUCNjfwJS8tIAA6Ssm2sdgyQVCni8Hc7BKFKNX1 K5NglEZ48XV9mJ8fUI4UxzPDeTdgN3SRcN9etttqo6cY4XEPyaHll0tlZGQu5L9IX381 d2VbtBez8MbYrP9Y1RRNjYWc3xoJt6mPzZ/lHmA6LUtm4AWi8L8evLCvsn43chdg3vji 0+Lg== X-Forwarded-Encrypted: i=1; AHgh+Rq5ehJnBriK+jFIh4WE5TmCJptCCqU9rXfURGjza8oW6fi4U/GLAV93TddfgysTMztF34bEHALXUu0N5Nk=@vger.kernel.org X-Gm-Message-State: AOJu0YwbMtL4ZYgRTqvWNeeXIS8DTQtUHjK92iBoN0RZJUD3M8TEF2iz KWxW+D5+cO9n1EKvbikEy7UKa/QdNveBmHyJBWc0QGZDGphyhenSXCes9Ns0VKtqfjo+UmbGz1P iW0PPKnUM X-Gm-Gg: AR+sD12CrB7wrLaNZ9Hux7VrAxhqmmMtJSi+v30QhPMbM8RfUaN/tNTbGsHyuREdIyv 9O8HGtMRaDO7qRr7M0W+/et2xJD0/eYegdDMQ2xTh+I/67IISTh21QtBSg49chfHF+RMhCauU7L J8qEaSfsvoRxhLlN9RuLZJoNGk26vBy/bJSCMlYZ/cpA3izly0yM5D0uxNIaRj8D60vCxmcVBtf y/62KvSRdmNjhYEeDPKsV/eija94oeDItf/31s4O+aP7U0gK1fZEt1cw7zoWN0jD7L82Lhf12Xf mwJ39f3EnXbnlm61eAErjW0sUvXnz2rE42ghqprvZUm7AR1aapH2UjlRRrFQkdlL6L564g64qCj 4qdaV/O0W6JeHZvTblFLUdoJH6DBlcOwgYtiuaw/H7A9SZxUvpx1Bs1vIcnEAwn6ZYqcfxzFXjw Ep1d93+QzyRDWLF2B9HmbCDa+g9zBN3GACG9/Spz6W+B477bcWirgGhfaZv2pajUn9bNXXcbrRd Wzq0oP+k77fN1uEUt07kndZP1utiq+92qVLZW4XRCle4RJu X-Received: by 2002:a05:600c:42d4:b0:495:5dad:b3de with SMTP id 5b1f17b1804b1-4995c0d3d01mr231515e9.10.1786047365829; Thu, 06 Aug 2026 13:16:05 -0700 (PDT) Received: from localhost ([2a00:79e0:288a:8:8b80:1ac5:3668:4014]) by smtp.gmail.com with ESMTPSA id 5b1f17b1804b1-4995bddfe99sm5333715e9.3.2026.08.06.13.16.04 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Thu, 06 Aug 2026 13:16:04 -0700 (PDT) From: Jann Horn Date: Thu, 06 Aug 2026 22:15:54 +0200 Subject: [PATCH] cred: clarify that task_struct::cred is only for the current task Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Message-Id: <20260806-cred-nonrcu-annotation-fix-v1-1-5fd149b2ab54@google.com> X-B4-Tracking: v=1; b=H4sIAHnrdGoC/yXMQQrCQAyF4auUrA2kU2nFq4iLmWnUuMhIZipC6 d0bdfk/eN8KlU24wrlbwfgtVYp69IcO8iPqnVFmbwgURjrRiNl4Ri1qecGoWlpsfsGbfDAmStM xTGnoCRx4Gfv8wy/Xf9clPTm3rwjbtgMkpm5XfgAAAA== X-Change-ID: 20260806-cred-nonrcu-annotation-fix-ab0b7427b310 To: Paul Moore , Serge Hallyn Cc: Eric Paris , James Morris , "Serge E. Hallyn" , linux-security-module@vger.kernel.org, linux-kernel@vger.kernel.org, audit@vger.kernel.org, Jann Horn X-Mailer: b4 0.15.2 X-Developer-Signature: v=1; a=ed25519-sha256; t=1786047359; l=5431; i=jannh@google.com; s=20240730; h=from:subject:message-id; bh=iO3qoa9nWB6AZNwYVsc67efJhEGt5zYcjAApR0Or+DQ=; b=QQnAkpll9HVAsA3Opte32VlGPtYA1gGl6MA7NGuCOE+bn4PHJ0wkZPykjncJG5jZWlPWYu0y2 sobJ58CChMKCcuZgyPl5A4070j8y050n4YLr6QgjzaFunlVNC45Bd2E X-Developer-Key: i=jannh@google.com; a=ed25519; pk=AljNtGOzXeF6khBXDJVVvwSEkVDGnnZZYqfWhP1V+C8= The `cred` field in task_struct is currently marked as __rcu, but that's not true: It can point to credentials from access_override_creds(), which do not get freed with RCU delay. What actually protects task_struct::cred is that accessing it is only permitted for the current task (except for setting up a child during fork() or tearing down a dead process). (There is currently code in Smack that violates this rule, but that's a bug and causes UAF, I have sent a separate fix for that.) Clarify this, remove the __rcu marker, and remove RCU helpers from all accesses to this field. Signed-off-by: Jann Horn Reviewed-by: Serge Hallyn --- For context: There have been at least two UAFs of struct cred that I'm aware of, both caused by wrong use of task_struct::cred: - https://git.kernel.org/linus/a3727a8bac0a9e77c70820655fd8715523ba3db7 - https://lore.kernel.org/all/20260806-smack-uaf-fix-v1-1-26426d389a26@goo= gle.com/ --- include/linux/cred.h | 17 +++++++++++------ include/linux/sched.h | 8 ++++++-- kernel/auditsc.c | 5 +++-- kernel/cred.c | 2 +- security/lsm_init.c | 2 +- 5 files changed, 22 insertions(+), 12 deletions(-) diff --git a/include/linux/cred.h b/include/linux/cred.h index c6676265a985..650aefd1416a 100644 --- a/include/linux/cred.h +++ b/include/linux/cred.h @@ -180,12 +180,18 @@ static inline bool cap_ambient_invariant_ok(const str= uct cred *cred) =20 static inline const struct cred *override_creds(const struct cred *overrid= e_cred) { - return rcu_replace_pointer(current->cred, override_cred, 1); + const struct cred *old =3D current->cred; + + current->cred =3D override_cred; + return old; } =20 static inline const struct cred *revert_creds(const struct cred *revert_cr= ed) { - return rcu_replace_pointer(current->cred, revert_cred, 1); + const struct cred *override_cred =3D current->cred; + + current->cred =3D revert_cred; + return override_cred; } =20 DEFINE_CLASS(override_creds, @@ -293,11 +299,10 @@ DEFINE_FREE(put_cred, struct cred *, if (!IS_ERR_OR_N= ULL(_T)) put_cred(_T)) /** * current_cred - Access the current task's subjective credentials * - * Access the subjective credentials of the current task. RCU-safe, - * since nobody else can modify it. + * Access the subjective credentials of the current task. + * Nobody else can modify it. */ -#define current_cred() \ - rcu_dereference_protected(current->cred, 1) +#define current_cred() (current->cred) =20 /** * current_real_cred - Access the current task's objective credentials diff --git a/include/linux/sched.h b/include/linux/sched.h index 373bcc0598d1..6f489c2cf5ea 100644 --- a/include/linux/sched.h +++ b/include/linux/sched.h @@ -1163,8 +1163,12 @@ struct task_struct { /* Objective and real subjective task credentials (COW): */ const struct cred __rcu *real_cred; =20 - /* Effective (overridable) subjective task credentials (COW): */ - const struct cred __rcu *cred; + /* + * Effective (overridable) subjective task credentials (COW). + * Only accessible for the current task and during task creation/freeing. + * This pointer is not managed by RCU! + */ + const struct cred *cred; =20 #ifdef CONFIG_KEYS /* Cached requested key. */ diff --git a/kernel/auditsc.c b/kernel/auditsc.c index 6610e667c728..646145a66196 100644 --- a/kernel/auditsc.c +++ b/kernel/auditsc.c @@ -459,7 +459,7 @@ static int audit_field_compare(struct task_struct *tsk, * * If task_creation is true, this is an explicit indication that we are * filtering a task rule at task creation time. This and tsk =3D=3D curre= nt are - * the only situations where tsk->cred may be accessed without an rcu read= lock. + * the only situations where tsk->cred may be accessed. */ static int audit_filter_rules(struct task_struct *tsk, struct audit_krule *rule, @@ -476,7 +476,8 @@ static int audit_filter_rules(struct task_struct *tsk, if (ctx && rule->prio <=3D ctx->prio) return 0; =20 - cred =3D rcu_dereference_check(tsk->cred, tsk =3D=3D current || task_crea= tion); + WARN_ON(tsk !=3D current && !task_creation); + cred =3D tsk->cred; =20 for (i =3D 0; i < rule->field_count; i++) { struct audit_field *f =3D &rule->fields[i]; diff --git a/kernel/cred.c b/kernel/cred.c index 3df4e15bd67f..0bd6a58bc12d 100644 --- a/kernel/cred.c +++ b/kernel/cred.c @@ -414,7 +414,7 @@ int commit_creds(struct cred *new) inc_rlimit_ucounts(new->ucounts, UCOUNT_RLIMIT_NPROC, 1); =20 rcu_assign_pointer(task->real_cred, new); - rcu_assign_pointer(task->cred, new); + task->cred =3D new; if (new->user !=3D old->user || new->user_ns !=3D old->user_ns) dec_rlimit_ucounts(old->ucounts, UCOUNT_RLIMIT_NPROC, 1); if (new->user_ns !=3D old->user_ns) diff --git a/security/lsm_init.c b/security/lsm_init.c index 7c0fd17f1601..1328a2ceef4b 100644 --- a/security/lsm_init.c +++ b/security/lsm_init.c @@ -476,7 +476,7 @@ int __init security_init(void) blob_sizes.lbs_inode, 0, SLAB_PANIC, NULL); =20 - if (lsm_cred_alloc((struct cred *)unrcu_pointer(current->cred), + if (lsm_cred_alloc((struct cred *)current->cred, GFP_KERNEL)) panic("early LSM cred alloc failed\n"); if (lsm_task_alloc(current)) --- base-commit: fcaeecb8b0cd44f77d03b28de0671258d4db18f8 change-id: 20260806-cred-nonrcu-annotation-fix-ab0b7427b310 Best regards, -- =20 Jann Horn