From nobody Sat Sep 26 06:20:28 2026 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-alma10-1.taild15c8.ts.net [100.103.45.18]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 601AF3630BF; Thu, 3 Sep 2026 23:24:39 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=100.103.45.18 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1788477880; cv=none; b=OZWn1qv5xret3+v3QTPrT3qPxvd5Df66S+nS/0k4jOazF67psBkVXAFicOBiyUCvEwu1D0hT6SLjo4YBYUoZSCDCgnd/uUQORsI+9ur0jmpQ9cUOrTOt/q8OpWNsj8fypqv1CRzuV4RXibqGTtH0uUKOLU8iTRcTah2Jkm+avCU= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1788477880; c=relaxed/simple; bh=r1vurSscIyny3Yx0Jf+hbRvdDU8fpobXkuTGkEBoLkA=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version; b=SnzUhUitRRRUcp8bfUpT+Hmud9/btGWedPTJ5p6/gb7cv/e+2XBc2DV2RYxsDbgzsTSOlXLQcmeUqDOzXI7sC1qE6AYzzz40Uf+TvgKFijy26y80eQm0V/08kK4BmiHVrSg1FMxlaEfcQLI5mY6zbFMt6E7gByeOki5w/8AE3Jk= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=PQqdPpF/; arc=none smtp.client-ip=100.103.45.18 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="PQqdPpF/" Received: by smtp.kernel.org (Postfix) with ESMTPSA id 172C81F00A3E; Thu, 3 Sep 2026 23:24:39 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=kernel.org; s=k20260515; t=1788477879; bh=1LW90y13IOdbJfNjSUxpwj36Qi9PJAz/jImCqGzw6do=; h=From:To:Cc:Subject:Date:In-Reply-To:References; b=PQqdPpF/lOhYgVvPkGqlXNzXFu1bi970wQZ/QPDMV8HwXsXHjR1H1bSt17Tu85lGE hcLYqkJJ1z+UqpvZZpPbBBPyfBOSk8/MhtHqxoUSW1qVzCuTvXh+SqULNUOhZ6Q9ix AMdeYlrhJxIi4w1q5a1QTBS5p/cXhF2iL7kovLs3TyblFspLgK9TZVLGLd4a+cAvzW I+gp2KscsMUpPKH+RIoMgkt9rY4EmyHYyinwtHixZtGRkxTDNoW04qHdMEJAXvK6X1 91yVBXKKV3E4Y8Kx1wldVQCb4GraKp9sbd5JJGpCQ2Jz7E7yYkOsShvfdgPSDLc0M2 hBVtYKOcURbeg== From: Kees Cook To: Bill Wendling Cc: Kees Cook , Vincent Mailhol , Nathan Chancellor , Nick Desaulniers , Justin Stitt , Greg Kroah-Hartman , linux-kernel@vger.kernel.org, linux-hardening@vger.kernel.org Subject: [PATCH 1/2] randstruct: fix container_of() false positives after __mptr removal Date: Thu, 3 Sep 2026 16:24:35 -0700 Message-Id: <20260903232438.60394-1-kees@kernel.org> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20260903232428.i.323-kees@kernel.org> References: <20260903232428.i.323-kees@kernel.org> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 X-Developer-Signature: v=1; a=openpgp-sha256; l=5737; i=kees@kernel.org; h=from:subject; bh=r1vurSscIyny3Yx0Jf+hbRvdDU8fpobXkuTGkEBoLkA=; b=owGbwMvMwCVmps19z/KJym7G02pJDFmzGDcnXm7xnJZlf8y2TG5+oBOj4fnSpz4ycbMsXiz7/ 4yPJUSoo5SFQYyLQVZMkSXIzj3OxeNte7j7XEWYOaxMIEMYuDgFYCKTexj+u1lw3Dvs5p9yI839 yhfmDd8Mb6293rnDOfVI+7PqJSoZcxkZ+pPE7tefebqvKmWB8a+zzIfnHA1sOLHup45B6nqNZTr vOQE= X-Developer-Key: i=kees@kernel.org; a=openpgp; fpr=A5C3F68F229DD60F723E6E138972F4DFDC6DC026 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" Commit f9e7a7564834 ("container_of: remove local __mptr variable") dropped the "void *__mptr" temporary from container_of(). The randstruct GCC plugin's find_bad_casts pass recognized the casts container_of() generates by that variable's name: const_tree ssa_name_var =3D SSA_NAME_VAR(rhs1); /* skip bogus type casts introduced by container_of */ if (ssa_name_var !=3D NULL_TREE && DECL_NAME(ssa_name_var) && !strcmp(DECL_NAME_POINTER(ssa_name_var), "__mptr")) continue; With the variable gone the suppression never fires, so every container_of() whose container type is randomized now emits a note: include/linux/container_of.h:23:9: note: randstruct: casting between randomized structure pointer types (ssa): 'struct ocfs2_triggers' and 'struct jbd2_buffer_trigger_type' fs/ocfs2/journal.c:524:16: note: in expansion of macro 'container_of' The pass runs on GIMPLE, after folding, and for a member at offset 0 the whole expression collapses to a bare copy that is indistinguishable from an unsafe cast: to_ocfs2_trigger (struct jbd2_buffer_trigger_type * triggers) { _2 =3D triggers_1(D); /* void * cast and subtraction gone */ return _2; } Match the type relationship instead. A cast from A * to B * is a container_of() if B reaches a field of type A at offset 0 through a chain of by-value members. The chain matters: container_of()'s member argument may be a dotted path, as in ceph_inode(), which is container_of(inode, struct ceph_inode_info, netfs.inode) and needs two levels. The search is depth-bounded to 4 just in case, since real paths are generally one or two members deep. Requiring the cast happens at offset 0 is done because any other member offset the subtraction survives folding and the cast's rhs is still void *, which the pass already skips a few lines above. A cast between two randomized types with no containment relationship is still reported. Verified with: struct cred *f(struct file *f) { return (struct cred *)f; } which is still flagged with the patch applied. Clang's implementation is unaffected. It checks the cast as written, and both the old and new macros cast from void *, which is always permitted; a genuinely bad cast is rejected there as a hard error rather than a note. Build tested ARCH=3Dx86_64 defconfig with CONFIG_RANDSTRUCT_FULL=3Dy and GCC 14.2.0: randstruct notes 52 before, 0 after. Fixes: f9e7a7564834 ("container_of: remove local __mptr variable") Assisted-by: Claude:claude-opus-5[1m] Signed-off-by: Kees Cook --- scripts/gcc-plugins/randomize_layout_plugin.c | 61 ++++++++++++++++++- 1 file changed, 58 insertions(+), 3 deletions(-) diff --git a/scripts/gcc-plugins/randomize_layout_plugin.c b/scripts/gcc-pl= ugins/randomize_layout_plugin.c index ff65a4f87f24..e2bd9ba08089 100644 --- a/scripts/gcc-plugins/randomize_layout_plugin.c +++ b/scripts/gcc-plugins/randomize_layout_plugin.c @@ -698,6 +698,63 @@ static void handle_local_var_initializers(void) } } =20 +/* + * Does @container reach a field of type @member_type by a chain of + * by-value members? That is the relationship container_of() expresses -- + * its @member argument may be a dotted path, e.g. + * container_of(inode, struct ceph_inode_info, netfs.inode) -- so a cast + * from @member_type * to @container * is legitimate rather than a + * layout-confusing one. + * + * container_of() used to leave a "void *__mptr" temporary behind, and this + * pass recognised such casts by that name. Commit f9e7a7564834 + * ("container_of: remove local __mptr variable") removed it to stop nested + * container_of() shadowing itself, and the cast now folds to a bare SSA + * copy when the member sits at offset 0, leaving nothing syntactic to key + * on. Match the type relationship instead. + * + * The depth bound keeps this cheap; container_of() paths are one or two + * members deep in practice. + */ +#define CONTAINER_OF_MAX_DEPTH 4 + +static bool is_container_of_cast(const_tree container, const_tree member_t= ype, + int depth) +{ + const_tree field; + + if (container =3D=3D NULL_TREE || depth > CONTAINER_OF_MAX_DEPTH) + return false; + + if (TREE_CODE(container) !=3D RECORD_TYPE && + TREE_CODE(container) !=3D UNION_TYPE) + return false; + + for (field =3D TYPE_FIELDS(container); field; field =3D DECL_CHAIN(field)= ) { + const_tree field_type; + + if (TREE_CODE(field) !=3D FIELD_DECL) + continue; + + /* + * Only a member at offset 0 can reach here: for any other + * offset container_of()'s subtraction survives folding, the + * cast's rhs stays void *, and the caller skipped it above. + */ + if (!integer_zerop(byte_position(field))) + continue; + + field_type =3D TYPE_MAIN_VARIANT(TREE_TYPE(field)); + if (field_type =3D=3D member_type) + return true; + + if (is_container_of_cast(field_type, member_type, depth + 1)) + return true; + } + + return false; +} + /* * iterate over all statements to find "bad" casts: * those where the address of the start of a structure is cast @@ -799,10 +856,8 @@ static unsigned int find_bad_casts_execute(void) #endif MISMATCH(gimple_location(stmt), "op0", ptr_lhs_type, op0_type); } else { - const_tree ssa_name_var =3D SSA_NAME_VAR(rhs1); /* skip bogus type casts introduced by container_of */ - if (ssa_name_var !=3D NULL_TREE && DECL_NAME(ssa_name_var) &&=20 - !strcmp((const char *)DECL_NAME_POINTER(ssa_name_var), "__mptr")) + if (is_container_of_cast(ptr_lhs_type, ptr_rhs_type, 0)) continue; #ifndef __DEBUG_PLUGIN if (lookup_attribute("randomize_performed", TYPE_ATTRIBUTES(ptr_rhs_ty= pe))) --=20 2.34.1 From nobody Sat Sep 26 06:20:28 2026 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-alma10-1.taild15c8.ts.net [100.103.45.18]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 6023B364EAF; Thu, 3 Sep 2026 23:24:39 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=100.103.45.18 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1788477880; cv=none; b=pQH7xdlbQKjj49szeEu523THf0PYNrZOWURtuhBLVuPOYosjmgFcInaFXrRf1zg6+mCLnD3pZvTTrAZU6/6bXZ/8ucXvYqk9QJbMe6xUgynQvH8VvuuhOxoixJxodsZngkfQVVezRcwgE9wZ1Eg/n62oixTzaoCRpkCkcvZx0jg= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1788477880; c=relaxed/simple; bh=7Fw6XpoEYxP+6q7RreDjmWqwEL2mZhyx41ZjWBSsbvM=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version; b=K9IlP+jpXHx2hNK4n83gNkhgMtt9jrFkG7Nzn/iQXf9HBYVdvcrgsuoduAqexYZaYHt/cSMI7OdXHQiSBgV4FeK5xH91WKxk2DafEWVB9mw8OHSjRWVOabk9Eya2fInNQUKAp0R17aBfFSWBoAGVNI9DTCk09JGQE9jnSzzDQrM= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=Fn9l52MY; arc=none smtp.client-ip=100.103.45.18 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="Fn9l52MY" Received: by smtp.kernel.org (Postfix) with ESMTPSA id 1519D1F00A3D; Thu, 3 Sep 2026 23:24:39 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=kernel.org; s=k20260515; t=1788477879; bh=o5mhh7f1v3xro5ujmd0JNWm/om76tra95+gEwAI9ekY=; h=From:To:Cc:Subject:Date:In-Reply-To:References; b=Fn9l52MY8x7nJzZOrX9R0v3oIP97l5bMMV+uVTi8DPrgx1i5lbAkcnzyEScrGh2+u SNhjWiQQ7e3nVLb8/luOyZAPyQ1bTZmJsPYtByVvuPawl/K9GNAKq3P3czri7lJyBk gtYQnOCPZ5iWGatLgcD4dTy1q88tSyh65BhxmF7iHA0GJSKgzWG9NFy7JBVni2OT2x ZUHnhjW/eFWTYTlK0BPsrvs/eq29F5BYl6chNN7GPAaNLiDKILp1lbkmvmmiJpa58t 5hXRoRGadaioOot6jCDcw6AXQEmQSlCPMPURcC+TKXr6ySPaahIIk31MFbfKWmRmPL PVO15LwAUIV9A== From: Kees Cook To: Bill Wendling Cc: Kees Cook , Vincent Mailhol , Nathan Chancellor , Nick Desaulniers , Justin Stitt , Greg Kroah-Hartman , linux-kernel@vger.kernel.org, linux-hardening@vger.kernel.org Subject: [PATCH 2/2] randstruct: report bad casts as warnings rather than notes Date: Thu, 3 Sep 2026 16:24:36 -0700 Message-Id: <20260903232438.60394-2-kees@kernel.org> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20260903232428.i.323-kees@kernel.org> References: <20260903232428.i.323-kees@kernel.org> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 X-Developer-Signature: v=1; a=openpgp-sha256; l=1707; i=kees@kernel.org; h=from:subject; bh=7Fw6XpoEYxP+6q7RreDjmWqwEL2mZhyx41ZjWBSsbvM=; b=owGbwMvMwCVmps19z/KJym7G02pJDFmzGLekckrcTvsT4JNXdSHoeDJzYOrU95MmRh+T+dJYv 8NsIo9yRykLgxgXg6yYIkuQnXuci8fb9nD3uYowc1iZQIYwcHEKwERU7zP890myrL0iu34vt4Hw x5j4c10MD7i+8NfMk+w4tXC3qiDDS4Z/BrkPs+KTv601bLiZP+1AnSDv7aJ2J3uVTZtPnpolebi KDQA= X-Developer-Key: i=kees@kernel.org; a=openpgp; fpr=A5C3F68F229DD60F723E6E138972F4DFDC6DC026 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" find_bad_casts() reports a cast between two randomized structure pointer types with inform(), which renders as a "note:". It has done so since the plugin was originally added, while using error() freely for attribute misuse, UAPI structs, and version mismatches. Clang's implementation of the same check has always been stricter: it rejects such a cast as a full error. There is no reason for the GCC side to be effectively silent about the same problem. Build tested ARCH=3Dx86_64 with CONFIG_RANDSTRUCT_FULL=3Dy and GCC 14.2.0: allmodconfig clean, and defconfig clean under three different random seeds. A deliberate bad cast is still reported, now as a warning, at the correct line and column. Signed-off-by: Kees Cook --- scripts/gcc-plugins/randomize_layout_plugin.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/gcc-plugins/randomize_layout_plugin.c b/scripts/gcc-pl= ugins/randomize_layout_plugin.c index e2bd9ba08089..1e66f45fe29b 100644 --- a/scripts/gcc-plugins/randomize_layout_plugin.c +++ b/scripts/gcc-plugins/randomize_layout_plugin.c @@ -22,7 +22,7 @@ #define ORIG_TYPE_NAME(node) \ (TYPE_NAME(TYPE_MAIN_VARIANT(node)) !=3D NULL_TREE ? ((const unsigned cha= r *)IDENTIFIER_POINTER(TYPE_NAME(TYPE_MAIN_VARIANT(node)))) : (const unsign= ed char *)"anonymous") =20 -#define INFORM(loc, msg, ...) inform(loc, "randstruct: " msg, ##__VA_ARGS_= _) +#define INFORM(loc, msg, ...) warning_at(loc, 0, "randstruct: " msg, ##__V= A_ARGS__) #define MISMATCH(loc, how, ...) INFORM(loc, "casting between randomized st= ructure pointer types (" how "): %qT and %qT\n", __VA_ARGS__) =20 __visible int plugin_is_GPL_compatible; --=20 2.34.1