diff options
| author | Kees Cook <kees@kernel.org> | 2026-09-03 16:24:35 -0700 |
|---|---|---|
| committer | Kees Cook <kees@kernel.org> | 2026-09-03 16:27:21 -0700 |
| commit | 3088a585de0eca79aa642ead0763f0642a1a1e52 (patch) | |
| tree | 58463ab860adad630d9d0e2f8fac700bd67e928e /scripts/Makefile.thinlto | |
| parent | 37eadcb919b02a742582de781097b455f7b16d76 (diff) | |
| download | linux-next-3088a585de0eca79aa642ead0763f0642a1a1e52.tar.gz linux-next-3088a585de0eca79aa642ead0763f0642a1a1e52.zip | |
randstruct: fix container_of() false positives after __mptr removal
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 = SSA_NAME_VAR(rhs1);
/* skip bogus type casts introduced by container_of */
if (ssa_name_var != 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 = 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=x86_64 defconfig with CONFIG_RANDSTRUCT_FULL=y 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]
Link: https://patch.msgid.link/20260903232438.60394-1-kees@kernel.org
Signed-off-by: Kees Cook <kees@kernel.org>
Diffstat (limited to 'scripts/Makefile.thinlto')
0 files changed, 0 insertions, 0 deletions
