Linux Security Modules development
 help / color / mirror / Atom feed
* Re: [PATCH v3 00/35] Compiler-Based Capability- and Locking-Analysis
From: Nathan Chancellor @ 2025-09-18 17:45 UTC (permalink / raw)
  To: Christoph Hellwig
  Cc: Marco Elver, Peter Zijlstra, Boqun Feng, Ingo Molnar, Will Deacon,
	David S. Miller, Luc Van Oostenryck, Paul E. McKenney,
	Alexander Potapenko, Arnd Bergmann, Bart Van Assche,
	Bill Wendling, Dmitry Vyukov, Eric Dumazet, Frederic Weisbecker,
	Greg Kroah-Hartman, Herbert Xu, Ian Rogers, Jann Horn,
	Joel Fernandes, Jonathan Corbet, Josh Triplett, Justin Stitt,
	Kees Cook, Kentaro Takeda, Lukas Bulwahn, Mark Rutland,
	Mathieu Desnoyers, Miguel Ojeda, Neeraj Upadhyay,
	Nick Desaulniers, Steven Rostedt, Tetsuo Handa, Thomas Gleixner,
	Thomas Graf, Uladzislau Rezki, Waiman Long, kasan-dev,
	linux-crypto, linux-doc, linux-kbuild, linux-kernel, linux-mm,
	linux-security-module, linux-sparse, llvm, rcu
In-Reply-To: <20250918141511.GA30263@lst.de>

On Thu, Sep 18, 2025 at 04:15:11PM +0200, Christoph Hellwig wrote:
> On Thu, Sep 18, 2025 at 03:59:11PM +0200, Marco Elver wrote:
> > A Clang version that supports `-Wthread-safety-pointer` and the new
> > alias-analysis of capability pointers is required (from this version
> > onwards):
> > 
> > 	https://github.com/llvm/llvm-project/commit/b4c98fcbe1504841203e610c351a3227f36c92a4 [3]
> 
> There's no chance to make say x86 pre-built binaries for that available?

I can use my existing kernel.org LLVM [1] build infrastructure to
generate prebuilt x86 binaries. Just give me a bit to build and upload
them. You may not be the only developer or maintainer who may want to
play with this.

[1]: https://kernel.org/pub/tools/llvm/

Cheers,
Nathan

^ permalink raw reply

* Re: [PATCH v4 22/34] lsm: group lsm_order_parse() with the other lsm_order_*() functions
From: Mimi Zohar @ 2025-09-18 17:22 UTC (permalink / raw)
  To: Paul Moore, linux-security-module, linux-integrity, selinux
  Cc: John Johansen, Roberto Sassu, Fan Wu, Mickaël Salaün,
	Günther Noack, Kees Cook, Micah Morton, Casey Schaufler,
	Tetsuo Handa, Nicolas Bouchinet, Xiu Jianfeng
In-Reply-To: <20250916220355.252592-58-paul@paul-moore.com>

On Tue, 2025-09-16 at 18:03 -0400, Paul Moore wrote:
> Move the lsm_order_parse() function near the other lsm_order_*()
> functions to improve readability.
> 
> No code changes.
> 
> Reviewed-by: Casey Schaufler <casey@schaufler-ca.com>
> Reviewed-by: John Johansen <john.johhansen@canonical.com>
> Signed-off-by: Paul Moore <paul@paul-moore.com>

Reviewed-by: Mimi Zohar <zohar@linux.ibm.com>

^ permalink raw reply

* Re: [PATCH v4 23/34] lsm: introduce an initcall mechanism into the LSM framework
From: Mimi Zohar @ 2025-09-18 17:19 UTC (permalink / raw)
  To: Paul Moore, linux-security-module, linux-integrity, selinux
  Cc: John Johansen, Roberto Sassu, Fan Wu, Mickaël Salaün,
	Günther Noack, Kees Cook, Micah Morton, Casey Schaufler,
	Tetsuo Handa, Nicolas Bouchinet, Xiu Jianfeng
In-Reply-To: <20250916220355.252592-59-paul@paul-moore.com>

On Tue, 2025-09-16 at 18:03 -0400, Paul Moore wrote:
> Currently the individual LSMs register their own initcalls, and while
> this should be harmless, it can be wasteful in the case where a LSM
> is disabled at boot as the initcall will still be executed.  This
> patch introduces support for managing the initcalls in the LSM
> framework, and future patches will convert the existing LSMs over to
> this new mechanism.
> 
> Only initcall types which are used by the current in-tree LSMs are
> supported, additional initcall types can easily be added in the future
> if needed.
> 
> Reviewed-by: Kees Cook <kees@kernel.org>
> Reviewed-by: Casey Schaufler <casey@schaufler-ca.com>
> Reviewed-by: John Johansen <john.johhansen@canonical.com>
> Signed-off-by: Paul Moore <paul@paul-moore.com>

Reviewed-by: Mimi Zohar <zohar@linux.ibm.com>

^ permalink raw reply

* Re: [PATCH v4 21/34] lsm: output available LSMs when debugging
From: Mimi Zohar @ 2025-09-18 17:11 UTC (permalink / raw)
  To: Paul Moore, linux-security-module, linux-integrity, selinux
  Cc: John Johansen, Roberto Sassu, Fan Wu, Mickaël Salaün,
	Günther Noack, Kees Cook, Micah Morton, Casey Schaufler,
	Tetsuo Handa, Nicolas Bouchinet, Xiu Jianfeng
In-Reply-To: <20250916220355.252592-57-paul@paul-moore.com>

On Tue, 2025-09-16 at 18:03 -0400, Paul Moore wrote:
> This will display all of the LSMs built into the kernel, regardless
> of if they are enabled or not.
> 
> Reviewed-by: Casey Schaufler <casey@schaufler-ca.com>
> Reviewed-by: John Johansen <john.johhansen@canonical.com>
> Signed-off-by: Paul Moore <paul@paul-moore.com>

Reviewed-by: Mimi Zohar <zohar@linux.ibm.com>



^ permalink raw reply

* Re: [PATCH v3 00/35] Compiler-Based Capability- and Locking-Analysis
From: Ian Rogers @ 2025-09-18 16:21 UTC (permalink / raw)
  To: Marco Elver
  Cc: Peter Zijlstra, Boqun Feng, Ingo Molnar, Will Deacon,
	David S. Miller, Luc Van Oostenryck, Paul E. McKenney,
	Alexander Potapenko, Arnd Bergmann, Bart Van Assche,
	Bill Wendling, Christoph Hellwig, Dmitry Vyukov, Eric Dumazet,
	Frederic Weisbecker, Greg Kroah-Hartman, Herbert Xu, Jann Horn,
	Joel Fernandes, Jonathan Corbet, Josh Triplett, Justin Stitt,
	Kees Cook, Kentaro Takeda, Lukas Bulwahn, Mark Rutland,
	Mathieu Desnoyers, Miguel Ojeda, Nathan Chancellor,
	Neeraj Upadhyay, Nick Desaulniers, Steven Rostedt, Tetsuo Handa,
	Thomas Gleixner, Thomas Graf, Uladzislau Rezki, Waiman Long,
	kasan-dev, linux-crypto, linux-doc, linux-kbuild, linux-kernel,
	linux-mm, linux-security-module, linux-sparse, llvm, rcu
In-Reply-To: <20250918140451.1289454-1-elver@google.com>

On Thu, Sep 18, 2025 at 7:05 AM Marco Elver <elver@google.com> wrote:
>
> Capability analysis is a C language extension, which enables statically
> checking that user-definable "capabilities" are acquired and released where
> required. An obvious application is lock-safety checking for the kernel's
> various synchronization primitives (each of which represents a "capability"),
> and checking that locking rules are not violated.
>
> Clang originally called the feature "Thread Safety Analysis" [1], with
> some terminology still using the thread-safety-analysis-only names. This
> was later changed and the feature became more flexible, gaining the
> ability to define custom "capabilities". Its foundations can be found in
> "capability systems" [2], used to specify the permissibility of
> operations to depend on some capability being held (or not held).
>
> Because the feature is not just able to express capabilities related to
> synchronization primitives, the naming chosen for the kernel departs
> from Clang's initial "Thread Safety" nomenclature and refers to the
> feature as "Capability Analysis" to avoid confusion. The implementation
> still makes references to the older terminology in some places, such as
> `-Wthread-safety` being the warning enabled option that also still
> appears in diagnostic messages.
>
> Enabling capability analysis can be seen as enabling a dialect of Linux
> C with a Capability System.
>
> Additional details can be found in the added kernel-doc documentation.
> An LWN article covered v2 of the series: https://lwn.net/Articles/1012990/
>
>  [1] https://clang.llvm.org/docs/ThreadSafetyAnalysis.html
>  [2] https://www.cs.cornell.edu/talc/papers/capabilities.pdf
>
> === Development Approach ===
>
> Prior art exists in the form of Sparse's context tracking. Locking
> annotations on functions exist, so the concept of analyzing locking rules
> is not foreign to the kernel's codebase.
>
> However, Clang's analysis is more complete vs. Sparse's, with the
> typical trade-offs in static analysis: improved completeness is
> sacrificed for more possible false positives or additional annotations
> required by the programmer. Numerous options exist to disable or opt out
> certain code from analysis.
>
> This series initially aimed to retain compatibility with Sparse, which
> can provide tree-wide analysis of a subset of the capability analysis
> introduced, but it was later decided to drop Sparse compatibility. For
> the most part, the new (and old) keywords used for annotations remain
> the same, and many of the pre-existing annotations remain valid.
>
> One big question is how to enable this feature, given we end up with a
> new dialect of C -- 2 approaches have been considered:
>
>   A. Tree-wide all-or-nothing approach. This approach requires tree-wide
>      changes, adding annotations or selective opt-outs. Making additional
>      primitives capability-enabled increases churn, esp. where maintainers
>      are unaware of the feature's existence and how to use it.
>
> Because we can't change the programming language (even if from one C
> dialect to another) of the kernel overnight, a different approach might
> cause less friction.
>
>   B. A selective, incremental, and much less intrusive approach.
>      Maintainers of subsystems opt in their modules or directories into
>      "capability analysis" (via Makefile):
>
>        CAPABILITY_ANALYSIS_foo.o := y   # foo.o only
>        CAPABILITY_ANALYSIS := y         # all TUs
>
>      Most (eventually all) synchronization primitives and more
>      capabilities (including ones that could track "irq disabled",
>      "preemption" disabled, etc.) could be supported.
>
> The approach taken by this series is B. This ensures that only
> subsystems where maintainers are willing to deal with any warnings are
> opted-in. Introducing the feature can be done incrementally, without
> large tree-wide changes and adding numerous opt-outs and annotations to
> the majority of code.
>
>   Note: Bart Van Assche concurrently worked on enabling -Wthread-safety:
>   https://lore.kernel.org/all/20250206175114.1974171-1-bvanassche@acm.org/
>   Bart's work has shown what it might take to go with approach A
>   (tree-wide, restricted to 'mutex' usage). This has shown that the
>   analysis finds real issues when applied to enough subsystems!  We hope
>   this serves as motivation to eventually enable the analysis in as many
>   subsystems as possible, particularly subsystems that are not as easily
>   tested by CI systems and test robots.
>
> === Initial Uses ===
>
> With this initial series, the following synchronization primitives are
> supported: `raw_spinlock_t`, `spinlock_t`, `rwlock_t`, `mutex`,
> `seqlock_t`, `bit_spinlock`, RCU, SRCU (`srcu_struct`), `rw_semaphore`,
> `local_lock_t`, `ww_mutex`.
>
> To demonstrate use of the feature on real kernel code, the series also
> enables capability analysis for the following subsystems:
>
>         * kernel/kcov
>         * kernel/kcsan
>         * kernel/sched/
>         * lib/rhashtable
>         * lib/stackdepot
>         * mm/kfence
>         * security/tomoyo
>         * crypto/
>
> The initial benefits are static detection of violations of locking
> rules. As more capabilities are added, we would see more static checking
> beyond what regular C can provide, all while remaining easy (read quick)
> to use via the Clang compiler.
>
>   Note: The kernel already provides dynamic analysis tools Lockdep and
>   KCSAN for lock-safety checking and data-race detection respectively.
>   Unlike those, Clang's capability analysis is a compile-time static
>   analysis with no runtime impact. The static analysis complements
>   existing dynamic analysis tools, as it may catch some issues before
>   even getting into a running kernel, but is *not* a replacement for
>   whole-kernel testing with the dynamic analysis tools enabled!
>
> === Appendix ===
>
> A Clang version that supports `-Wthread-safety-pointer` and the new
> alias-analysis of capability pointers is required (from this version
> onwards):
>
>         https://github.com/llvm/llvm-project/commit/b4c98fcbe1504841203e610c351a3227f36c92a4 [3]
>
> This series is also available at this Git tree:
>
>         https://git.kernel.org/pub/scm/linux/kernel/git/melver/linux.git/log/?h=cap-analysis/dev
>
> === Changelog ===
>
> v3:
>
>   - Bump min. Clang version to 22+ (unreleased), which now supports:
>
>         * re-entrancy via __attribute__((reentrant_capability));
>         * basic form of capability alias analysis [3] - which is the
>           biggest improvement since v2.
>
>     This was the result of conclusions from this discussion:
>     https://lore.kernel.org/all/CANpmjNPquO=W1JAh1FNQb8pMQjgeZAKCPQUAd7qUg=5pjJ6x=Q@mail.gmail.com/
>
>   - Rename __asserts_cap/__assert_cap to __assumes_cap/__assume_cap.
>
>   - Switch to DECLARE_LOCK_GUARD_1_ATTRS().
>
>   - Add __acquire_ret and __acquire_shared_ret helper macros - can be
>     used to define function-like macros that return objects which
>     contains a held capabilities. Works now because of capability alias
>     analysis.
>
>   - Add capability_unsafe_alias() helper, where the analysis rightfully
>     points out we're doing strange things with aliases but we don't
>     care.
>
>   - Support multi-argument attributes.
>
>   - Enable for kernel/sched/{core,fair}.c, kernel/kcsan.
>   - Drop drivers/tty changes (revisit later).
>
> v2: https://lore.kernel.org/all/20250304092417.2873893-1-elver@google.com/
>
>   - Remove Sparse context tracking support - after the introduction of
>     Clang support, so that backports can skip removal of Sparse support.
>
>   - Remove __cond_lock() function-like helper.
>
>   - ww_mutex support.
>
>   - -Wthread-safety-addressof was reworked and committed in upstream
>     Clang as -Wthread-safety-pointer.
>
>   - Make __cond_acquires() and __cond_acquires_shared() take abstract
>     value, since compiler only cares about zero and non-zero.
>
>   - Rename __var_guarded_by to simply __guarded_by. Initially the idea
>     was to be explicit about if the variable itself or the pointed-to
>     data is guarded, but in the long-term, making this shorter might be
>     better.
>
>   - Likewise rename __ref_guarded_by to __pt_guarded_by.
>
>   - Introduce common header warning suppressions - this is a better
>     solution than guarding header inclusions with disable_ +
>     enable_capability_analysis(). Header suppressions are disabled when
>     selecting CONFIG_WARN_CAPABILITY_ANALYSIS_ALL=y. This bumps the
>     minimum Clang version required to 20+.
>
>   - Make the data_race() macro imply disabled capability analysis.
>     Writing capability_unsafe(data_race(..)) is unnecessarily verbose
>     and data_race() on its own already indicates something subtly unsafe
>     is happening.  This change was made after analysis of a finding in
>     security/tomoyo.
>
>   - Enable analysis in the following subsystems as additional examples
>     of larger subsystem. Where it was obvious, the __guarded_by
>     attribute was added to lock-guarded variables to improve coverage.
>
>         * drivers/tty
>         * security/tomoyo
>         * crypto/
>
> RFC v1: https://lore.kernel.org/lkml/20250206181711.1902989-1-elver@google.com

Thanks for this and lgtm. Fwiw, there is already thread safety
analysis in tools/perf:
https://git.kernel.org/pub/scm/linux/kernel/git/perf/perf-tools-next.git/tree/tools/perf/util/mutex.h?h=perf-tools-next#n43
and we should migrate that code to use this code.

Something that I've wondered about capabilities is to use them for
detecting missing reference count "puts", which feel similar to missed
unlocks. In my experience the sanitizers are weak in this area as in
C++ you can trivially use RAII, however, frustratingly clang's
capability analysis is disabled in C++'s constructors and destructors
(not an issue here :-) and based on my rusty memory). To solve this
for perf (and fix many many bugs) we did a form of runtime RAII:
https://perfwiki.github.io/main/reference-count-checking/
There is likely something better than can be done with the nearly RAII
that is/are cleanups. Trying to make that sane for a data-structure
like an rbtree is hard and maybe rust is just the only solution there.
Anyway, it is great to see thread safety analysis pushed forward.

Thanks,
Ian

> Marco Elver (35):
>   compiler_types: Move lock checking attributes to
>     compiler-capability-analysis.h
>   compiler-capability-analysis: Add infrastructure for Clang's
>     capability analysis
>   compiler-capability-analysis: Add test stub
>   Documentation: Add documentation for Compiler-Based Capability
>     Analysis
>   checkpatch: Warn about capability_unsafe() without comment
>   cleanup: Basic compatibility with capability analysis
>   lockdep: Annotate lockdep assertions for capability analysis
>   locking/rwlock, spinlock: Support Clang's capability analysis
>   compiler-capability-analysis: Change __cond_acquires to take return
>     value
>   locking/mutex: Support Clang's capability analysis
>   locking/seqlock: Support Clang's capability analysis
>   bit_spinlock: Include missing <asm/processor.h>
>   bit_spinlock: Support Clang's capability analysis
>   rcu: Support Clang's capability analysis
>   srcu: Support Clang's capability analysis
>   kref: Add capability-analysis annotations
>   locking/rwsem: Support Clang's capability analysis
>   locking/local_lock: Include missing headers
>   locking/local_lock: Support Clang's capability analysis
>   locking/ww_mutex: Support Clang's capability analysis
>   debugfs: Make debugfs_cancellation a capability struct
>   compiler-capability-analysis: Remove Sparse support
>   compiler-capability-analysis: Remove __cond_lock() function-like
>     helper
>   compiler-capability-analysis: Introduce header suppressions
>   compiler: Let data_race() imply disabled capability analysis
>   MAINTAINERS: Add entry for Capability Analysis
>   kfence: Enable capability analysis
>   kcov: Enable capability analysis
>   kcsan: Enable capability analysis
>   stackdepot: Enable capability analysis
>   rhashtable: Enable capability analysis
>   printk: Move locking annotation to printk.c
>   security/tomoyo: Enable capability analysis
>   crypto: Enable capability analysis
>   sched: Enable capability analysis for core.c and fair.c
>
>  .../dev-tools/capability-analysis.rst         | 148 +++++
>  Documentation/dev-tools/index.rst             |   1 +
>  Documentation/dev-tools/sparse.rst            |  19 -
>  Documentation/mm/process_addrs.rst            |   6 +-
>  MAINTAINERS                                   |  11 +
>  Makefile                                      |   1 +
>  crypto/Makefile                               |   2 +
>  crypto/acompress.c                            |   6 +-
>  crypto/algapi.c                               |   2 +
>  crypto/api.c                                  |   1 +
>  crypto/crypto_engine.c                        |   2 +-
>  crypto/drbg.c                                 |   5 +
>  crypto/internal.h                             |   2 +-
>  crypto/proc.c                                 |   3 +
>  crypto/scompress.c                            |  24 +-
>  .../net/wireless/intel/iwlwifi/iwl-trans.c    |   4 +-
>  .../net/wireless/intel/iwlwifi/iwl-trans.h    |   6 +-
>  .../intel/iwlwifi/pcie/gen1_2/internal.h      |   5 +-
>  .../intel/iwlwifi/pcie/gen1_2/trans.c         |   4 +-
>  fs/dlm/lock.c                                 |   2 +-
>  include/crypto/internal/acompress.h           |   7 +-
>  include/crypto/internal/engine.h              |   2 +-
>  include/linux/bit_spinlock.h                  |  24 +-
>  include/linux/cleanup.h                       |  17 +
>  include/linux/compiler-capability-analysis.h  | 423 +++++++++++++
>  include/linux/compiler.h                      |   2 +
>  include/linux/compiler_types.h                |  18 +-
>  include/linux/console.h                       |   4 +-
>  include/linux/debugfs.h                       |  12 +-
>  include/linux/kref.h                          |   2 +
>  include/linux/list_bl.h                       |   2 +
>  include/linux/local_lock.h                    |  45 +-
>  include/linux/local_lock_internal.h           |  73 ++-
>  include/linux/lockdep.h                       |  12 +-
>  include/linux/mm.h                            |  33 +-
>  include/linux/mutex.h                         |  35 +-
>  include/linux/mutex_types.h                   |   4 +-
>  include/linux/rcupdate.h                      |  86 +--
>  include/linux/refcount.h                      |   6 +-
>  include/linux/rhashtable.h                    |  14 +-
>  include/linux/rwlock.h                        |  22 +-
>  include/linux/rwlock_api_smp.h                |  43 +-
>  include/linux/rwlock_rt.h                     |  44 +-
>  include/linux/rwlock_types.h                  |  10 +-
>  include/linux/rwsem.h                         |  66 +-
>  include/linux/sched.h                         |   6 +-
>  include/linux/sched/signal.h                  |  16 +-
>  include/linux/sched/task.h                    |   5 +-
>  include/linux/sched/wake_q.h                  |   3 +
>  include/linux/seqlock.h                       |  24 +
>  include/linux/seqlock_types.h                 |   5 +-
>  include/linux/spinlock.h                      |  89 ++-
>  include/linux/spinlock_api_smp.h              |  34 +-
>  include/linux/spinlock_api_up.h               | 112 +++-
>  include/linux/spinlock_rt.h                   |  37 +-
>  include/linux/spinlock_types.h                |  10 +-
>  include/linux/spinlock_types_raw.h            |   5 +-
>  include/linux/srcu.h                          |  60 +-
>  include/linux/srcutiny.h                      |   4 +
>  include/linux/srcutree.h                      |   6 +-
>  include/linux/ww_mutex.h                      |  22 +-
>  kernel/Makefile                               |   2 +
>  kernel/kcov.c                                 |  36 +-
>  kernel/kcsan/Makefile                         |   2 +
>  kernel/kcsan/report.c                         |  11 +-
>  kernel/printk/printk.c                        |   2 +
>  kernel/sched/Makefile                         |   3 +
>  kernel/sched/core.c                           |  89 ++-
>  kernel/sched/fair.c                           |   9 +-
>  kernel/sched/sched.h                          | 110 +++-
>  kernel/signal.c                               |   4 +-
>  kernel/time/posix-timers.c                    |  13 +-
>  lib/Kconfig.debug                             |  45 ++
>  lib/Makefile                                  |   6 +
>  lib/dec_and_lock.c                            |   8 +-
>  lib/rhashtable.c                              |   5 +-
>  lib/stackdepot.c                              |  20 +-
>  lib/test_capability-analysis.c                | 596 ++++++++++++++++++
>  mm/kfence/Makefile                            |   2 +
>  mm/kfence/core.c                              |  20 +-
>  mm/kfence/kfence.h                            |  14 +-
>  mm/kfence/report.c                            |   4 +-
>  mm/memory.c                                   |   4 +-
>  mm/pgtable-generic.c                          |  19 +-
>  net/ipv4/tcp_sigpool.c                        |   2 +-
>  scripts/Makefile.capability-analysis          |  11 +
>  scripts/Makefile.lib                          |  10 +
>  scripts/capability-analysis-suppression.txt   |  33 +
>  scripts/checkpatch.pl                         |   8 +
>  security/tomoyo/Makefile                      |   2 +
>  security/tomoyo/common.c                      |  52 +-
>  security/tomoyo/common.h                      |  77 +--
>  security/tomoyo/domain.c                      |   1 +
>  security/tomoyo/environ.c                     |   1 +
>  security/tomoyo/file.c                        |   5 +
>  security/tomoyo/gc.c                          |  28 +-
>  security/tomoyo/mount.c                       |   2 +
>  security/tomoyo/network.c                     |   3 +
>  tools/include/linux/compiler_types.h          |   2 -
>  99 files changed, 2370 insertions(+), 589 deletions(-)
>  create mode 100644 Documentation/dev-tools/capability-analysis.rst
>  create mode 100644 include/linux/compiler-capability-analysis.h
>  create mode 100644 lib/test_capability-analysis.c
>  create mode 100644 scripts/Makefile.capability-analysis
>  create mode 100644 scripts/capability-analysis-suppression.txt
>
> --
> 2.51.0.384.g4c02a37b29-goog
>

^ permalink raw reply

* Re: [PATCH v3 02/35] compiler-capability-analysis: Add infrastructure for Clang's capability analysis
From: Steven Rostedt @ 2025-09-18 16:14 UTC (permalink / raw)
  To: Bart Van Assche
  Cc: Ian Rogers, Marco Elver, Peter Zijlstra, Boqun Feng, Ingo Molnar,
	Will Deacon, David S. Miller, Luc Van Oostenryck,
	Paul E. McKenney, Alexander Potapenko, Arnd Bergmann,
	Bill Wendling, Christoph Hellwig, Dmitry Vyukov, Eric Dumazet,
	Frederic Weisbecker, Greg Kroah-Hartman, Herbert Xu, Jann Horn,
	Joel Fernandes, Jonathan Corbet, Josh Triplett, Justin Stitt,
	Kees Cook, Kentaro Takeda, Lukas Bulwahn, Mark Rutland,
	Mathieu Desnoyers, Miguel Ojeda, Nathan Chancellor,
	Neeraj Upadhyay, Nick Desaulniers, Tetsuo Handa, Thomas Gleixner,
	Thomas Graf, Uladzislau Rezki, Waiman Long, kasan-dev,
	linux-crypto, linux-doc, linux-kbuild, linux-kernel, linux-mm,
	linux-security-module, linux-sparse, llvm, rcu
In-Reply-To: <1ca90ba0-7bdc-43d1-af12-bba73dd3234a@acm.org>

On Thu, 18 Sep 2025 09:03:17 -0700
Bart Van Assche <bvanassche@acm.org> wrote:

> On 9/18/25 8:58 AM, Ian Rogers wrote:
> > On Thu, Sep 18, 2025 at 7:05 AM Marco Elver <elver@google.com> wrote:  
> >> +config WARN_CAPABILITY_ANALYSIS
> >> +       bool "Compiler capability-analysis warnings"
> >> +       depends on CC_IS_CLANG && CLANG_VERSION >= 220000
> >> +       # Branch profiling re-defines "if", which messes with the compiler's
> >> +       # ability to analyze __cond_acquires(..), resulting in false positives.
> >> +       depends on !TRACE_BRANCH_PROFILING  
> > 
> > Err, wow! What and huh, and why? Crikes. I'm amazed you found such an
> > option exists. I must be very naive to have never heard of it and now
> > I wonder if it is needed and load bearing?  
> 
> (+Steven)
> 
> This is an old option. I think this commit introduced it:
> 
> commit 52f232cb720a7babb752849cbc2cab2d24021209
> Author: Steven Rostedt <rostedt@goodmis.org>
> Date:   Wed Nov 12 00:14:40 2008 -0500
> 
>      tracing: likely/unlikely branch annotation tracer
> 

I still use it every year (enable it during December for a few weeks on my
workstation and server) and post the results publicly. When I get time, I
try to fix (add / remove) likely/unlikely statements. Unfortunately, I
haven't had time to look deeper at them.

 https://rostedt.org/branches/current/

This year I missed December and ended up running it in January instead.

-- Steve

^ permalink raw reply

* Re: [PATCH v3 02/35] compiler-capability-analysis: Add infrastructure for Clang's capability analysis
From: Bart Van Assche @ 2025-09-18 16:03 UTC (permalink / raw)
  To: Ian Rogers, Marco Elver
  Cc: Peter Zijlstra, Boqun Feng, Ingo Molnar, Will Deacon,
	David S. Miller, Luc Van Oostenryck, Paul E. McKenney,
	Alexander Potapenko, Arnd Bergmann, Bill Wendling,
	Christoph Hellwig, Dmitry Vyukov, Eric Dumazet,
	Frederic Weisbecker, Greg Kroah-Hartman, Herbert Xu, Jann Horn,
	Joel Fernandes, Jonathan Corbet, Josh Triplett, Justin Stitt,
	Kees Cook, Kentaro Takeda, Lukas Bulwahn, Mark Rutland,
	Mathieu Desnoyers, Miguel Ojeda, Nathan Chancellor,
	Neeraj Upadhyay, Nick Desaulniers, Steven Rostedt, Tetsuo Handa,
	Thomas Gleixner, Thomas Graf, Uladzislau Rezki, Waiman Long,
	kasan-dev, linux-crypto, linux-doc, linux-kbuild, linux-kernel,
	linux-mm, linux-security-module, linux-sparse, llvm, rcu,
	Steven Rostedt
In-Reply-To: <CAP-5=fUfbMAKrLC_z04o9r0kGZ02tpHfv8cOecQAQaYPx44awA@mail.gmail.com>

On 9/18/25 8:58 AM, Ian Rogers wrote:
> On Thu, Sep 18, 2025 at 7:05 AM Marco Elver <elver@google.com> wrote:
>> +config WARN_CAPABILITY_ANALYSIS
>> +       bool "Compiler capability-analysis warnings"
>> +       depends on CC_IS_CLANG && CLANG_VERSION >= 220000
>> +       # Branch profiling re-defines "if", which messes with the compiler's
>> +       # ability to analyze __cond_acquires(..), resulting in false positives.
>> +       depends on !TRACE_BRANCH_PROFILING
> 
> Err, wow! What and huh, and why? Crikes. I'm amazed you found such an
> option exists. I must be very naive to have never heard of it and now
> I wonder if it is needed and load bearing?

(+Steven)

This is an old option. I think this commit introduced it:

commit 52f232cb720a7babb752849cbc2cab2d24021209
Author: Steven Rostedt <rostedt@goodmis.org>
Date:   Wed Nov 12 00:14:40 2008 -0500

     tracing: likely/unlikely branch annotation tracer

Bart.

^ permalink raw reply

* Re: [PATCH v3 02/35] compiler-capability-analysis: Add infrastructure for Clang's capability analysis
From: Ian Rogers @ 2025-09-18 15:58 UTC (permalink / raw)
  To: Marco Elver
  Cc: Peter Zijlstra, Boqun Feng, Ingo Molnar, Will Deacon,
	David S. Miller, Luc Van Oostenryck, Paul E. McKenney,
	Alexander Potapenko, Arnd Bergmann, Bart Van Assche,
	Bill Wendling, Christoph Hellwig, Dmitry Vyukov, Eric Dumazet,
	Frederic Weisbecker, Greg Kroah-Hartman, Herbert Xu, Jann Horn,
	Joel Fernandes, Jonathan Corbet, Josh Triplett, Justin Stitt,
	Kees Cook, Kentaro Takeda, Lukas Bulwahn, Mark Rutland,
	Mathieu Desnoyers, Miguel Ojeda, Nathan Chancellor,
	Neeraj Upadhyay, Nick Desaulniers, Steven Rostedt, Tetsuo Handa,
	Thomas Gleixner, Thomas Graf, Uladzislau Rezki, Waiman Long,
	kasan-dev, linux-crypto, linux-doc, linux-kbuild, linux-kernel,
	linux-mm, linux-security-module, linux-sparse, llvm, rcu
In-Reply-To: <20250918140451.1289454-3-elver@google.com>

On Thu, Sep 18, 2025 at 7:05 AM Marco Elver <elver@google.com> wrote:
>
> Capability analysis is a C language extension, which enables statically
> checking that user-definable "capabilities" are acquired and released where
> required. An obvious application is lock-safety checking for the kernel's
> various synchronization primitives (each of which represents a "capability"),
> and checking that locking rules are not violated.
>
> Clang originally called the feature "Thread Safety Analysis" [1], with
> some terminology still using the thread-safety-analysis-only names. This
> was later changed and the feature became more flexible, gaining the
> ability to define custom "capabilities". Its foundations can be found in
> "capability systems", used to specify the permissibility of operations
> to depend on some capability being held (or not held).
>
> [1] https://clang.llvm.org/docs/ThreadSafetyAnalysis.html
> [2] https://www.cs.cornell.edu/talc/papers/capabilities.pdf
>
> Because the feature is not just able to express capabilities related to
> synchronization primitives, the naming chosen for the kernel departs
> from Clang's initial "Thread Safety" nomenclature and refers to the
> feature as "Capability Analysis" to avoid confusion. The implementation
> still makes references to the older terminology in some places, such as
> `-Wthread-safety` being the warning enabled option that also still
> appears in diagnostic messages.
>
> See more details in the kernel-doc documentation added in this and the
> subsequent changes.
>
> Clang version 22+ is required.
>
> Signed-off-by: Marco Elver <elver@google.com>
> ---
> v3:
> * Require Clang 22 or later (reentrant capabilities, basic alias analysis).
> * Rename __assert_cap/__asserts_cap -> __assume_cap/__assumes_cap (suggested by Peter).
> * Add __acquire_ret and __acquire_shared_ret helper macros - can be used
>   to define function-like macros that return objects which contains a
>   held capabilities. Works now because of capability alias analysis.
> * Add capability_unsafe_alias() helper, where the analysis rightfully
>   points out we're doing strange things with aliases but we don't care.
> * Support multi-argument attributes.
>
> v2:
> * New -Wthread-safety feature rename to -Wthread-safety-pointer (was
>   -Wthread-safety-addressof).
> * Introduce __capability_unsafe() function attribute.
> * Rename __var_guarded_by to simply __guarded_by. The initial idea was
>   to be explicit if the variable or pointed-to data is guarded by, but
>   having a shorter attribute name is likely better long-term.
> * Rename __ref_guarded_by to __pt_guarded_by (pointed-to guarded by).
> ---
>  Makefile                                     |   1 +
>  include/linux/compiler-capability-analysis.h | 449 ++++++++++++++++++-
>  lib/Kconfig.debug                            |  31 ++
>  scripts/Makefile.capability-analysis         |   7 +
>  scripts/Makefile.lib                         |  10 +
>  5 files changed, 491 insertions(+), 7 deletions(-)
>  create mode 100644 scripts/Makefile.capability-analysis
>
> diff --git a/Makefile b/Makefile
> index cf37b9407821..2c91730e513b 100644
> --- a/Makefile
> +++ b/Makefile
> @@ -1096,6 +1096,7 @@ include-$(CONFIG_RANDSTRUCT)      += scripts/Makefile.randstruct
>  include-$(CONFIG_KSTACK_ERASE) += scripts/Makefile.kstack_erase
>  include-$(CONFIG_AUTOFDO_CLANG)        += scripts/Makefile.autofdo
>  include-$(CONFIG_PROPELLER_CLANG)      += scripts/Makefile.propeller
> +include-$(CONFIG_WARN_CAPABILITY_ANALYSIS) += scripts/Makefile.capability-analysis
>  include-$(CONFIG_GCC_PLUGINS)  += scripts/Makefile.gcc-plugins
>
>  include $(addprefix $(srctree)/, $(include-y))
> diff --git a/include/linux/compiler-capability-analysis.h b/include/linux/compiler-capability-analysis.h
> index 7546ddb83f86..6f3f185478bc 100644
> --- a/include/linux/compiler-capability-analysis.h
> +++ b/include/linux/compiler-capability-analysis.h
> @@ -6,27 +6,462 @@
>  #ifndef _LINUX_COMPILER_CAPABILITY_ANALYSIS_H
>  #define _LINUX_COMPILER_CAPABILITY_ANALYSIS_H
>
> +#if defined(WARN_CAPABILITY_ANALYSIS)
> +
> +/*
> + * The below attributes are used to define new capability types. Internal only.
> + */
> +# define __cap_type(name)                      __attribute__((capability(#name)))
> +# define __reentrant_cap                       __attribute__((reentrant_capability))
> +# define __acquires_cap(...)                   __attribute__((acquire_capability(__VA_ARGS__)))
> +# define __acquires_shared_cap(...)            __attribute__((acquire_shared_capability(__VA_ARGS__)))
> +# define __try_acquires_cap(ret, var)          __attribute__((try_acquire_capability(ret, var)))
> +# define __try_acquires_shared_cap(ret, var)   __attribute__((try_acquire_shared_capability(ret, var)))
> +# define __releases_cap(...)                   __attribute__((release_capability(__VA_ARGS__)))
> +# define __releases_shared_cap(...)            __attribute__((release_shared_capability(__VA_ARGS__)))
> +# define __assumes_cap(...)                    __attribute__((assert_capability(__VA_ARGS__)))
> +# define __assumes_shared_cap(...)             __attribute__((assert_shared_capability(__VA_ARGS__)))
> +# define __returns_cap(var)                    __attribute__((lock_returned(var)))
> +
> +/*
> + * The below are used to annotate code being checked. Internal only.
> + */
> +# define __excludes_cap(...)           __attribute__((locks_excluded(__VA_ARGS__)))
> +# define __requires_cap(...)           __attribute__((requires_capability(__VA_ARGS__)))
> +# define __requires_shared_cap(...)    __attribute__((requires_shared_capability(__VA_ARGS__)))
> +
> +/**
> + * __guarded_by - struct member and globals attribute, declares variable
> + *                protected by capability
> + *
> + * Declares that the struct member or global variable must be guarded by the
> + * given capabilities. Read operations on the data require shared access,
> + * while write operations require exclusive access.
> + *
> + * .. code-block:: c
> + *
> + *     struct some_state {
> + *             spinlock_t lock;
> + *             long counter __guarded_by(&lock);
> + *     };
> + */
> +# define __guarded_by(...)             __attribute__((guarded_by(__VA_ARGS__)))
> +
> +/**
> + * __pt_guarded_by - struct member and globals attribute, declares pointed-to
> + *                   data is protected by capability
> + *
> + * Declares that the data pointed to by the struct member pointer or global
> + * pointer must be guarded by the given capabilities. Read operations on the
> + * data require shared access, while write operations require exclusive access.
> + *
> + * .. code-block:: c
> + *
> + *     struct some_state {
> + *             spinlock_t lock;
> + *             long *counter __pt_guarded_by(&lock);
> + *     };
> + */
> +# define __pt_guarded_by(...)          __attribute__((pt_guarded_by(__VA_ARGS__)))
> +
> +/**
> + * struct_with_capability() - declare or define a capability struct
> + * @name: struct name
> + *
> + * Helper to declare or define a struct type with capability of the same name.
> + *
> + * .. code-block:: c
> + *
> + *     struct_with_capability(my_handle) {
> + *             int foo;
> + *             long bar;
> + *     };
> + *
> + *     struct some_state {
> + *             ...
> + *     };
> + *     // ... declared elsewhere ...
> + *     struct_with_capability(some_state);
> + *
> + * Note: The implementation defines several helper functions that can acquire,
> + * release, and assert the capability.
> + */
> +# define struct_with_capability(name, ...)                                                             \
> +       struct __cap_type(name) __VA_ARGS__ name;                                                       \
> +       static __always_inline void __acquire_cap(const struct name *var)                               \
> +               __attribute__((overloadable)) __no_capability_analysis __acquires_cap(var) { }          \
> +       static __always_inline void __acquire_shared_cap(const struct name *var)                        \
> +               __attribute__((overloadable)) __no_capability_analysis __acquires_shared_cap(var) { }   \
> +       static __always_inline bool __try_acquire_cap(const struct name *var, bool ret)                 \
> +               __attribute__((overloadable)) __no_capability_analysis __try_acquires_cap(1, var)       \
> +       { return ret; }                                                                                 \
> +       static __always_inline bool __try_acquire_shared_cap(const struct name *var, bool ret)          \
> +               __attribute__((overloadable)) __no_capability_analysis __try_acquires_shared_cap(1, var) \
> +       { return ret; }                                                                                 \
> +       static __always_inline void __release_cap(const struct name *var)                               \
> +               __attribute__((overloadable)) __no_capability_analysis __releases_cap(var) { }          \
> +       static __always_inline void __release_shared_cap(const struct name *var)                        \
> +               __attribute__((overloadable)) __no_capability_analysis __releases_shared_cap(var) { }   \
> +       static __always_inline void __assume_cap(const struct name *var)                                \
> +               __attribute__((overloadable)) __assumes_cap(var) { }                                    \
> +       static __always_inline void __assume_shared_cap(const struct name *var)                         \
> +               __attribute__((overloadable)) __assumes_shared_cap(var) { }                             \
> +       struct name
> +
> +/**
> + * disable_capability_analysis() - disables capability analysis
> + *
> + * Disables capability analysis. Must be paired with a later
> + * enable_capability_analysis().
> + */
> +# define disable_capability_analysis()                         \
> +       __diag_push();                                          \
> +       __diag_ignore_all("-Wunknown-warning-option", "")       \
> +       __diag_ignore_all("-Wthread-safety", "")                \
> +       __diag_ignore_all("-Wthread-safety-pointer", "")
> +
> +/**
> + * enable_capability_analysis() - re-enables capability analysis
> + *
> + * Re-enables capability analysis. Must be paired with a prior
> + * disable_capability_analysis().
> + */
> +# define enable_capability_analysis() __diag_pop()
> +
> +/**
> + * __no_capability_analysis - function attribute, disables capability analysis
> + *
> + * Function attribute denoting that capability analysis is disabled for the
> + * whole function. Prefer use of `capability_unsafe()` where possible.
> + */
> +# define __no_capability_analysis      __attribute__((no_thread_safety_analysis))
> +
> +#else /* !WARN_CAPABILITY_ANALYSIS */
> +
> +# define __cap_type(name)
> +# define __reentrant_cap
> +# define __acquires_cap(...)
> +# define __acquires_shared_cap(...)
> +# define __try_acquires_cap(ret, var)
> +# define __try_acquires_shared_cap(ret, var)
> +# define __releases_cap(...)
> +# define __releases_shared_cap(...)
> +# define __assumes_cap(...)
> +# define __assumes_shared_cap(...)
> +# define __returns_cap(var)
> +# define __guarded_by(...)
> +# define __pt_guarded_by(...)
> +# define __excludes_cap(...)
> +# define __requires_cap(...)
> +# define __requires_shared_cap(...)
> +# define __acquire_cap(var)                    do { } while (0)
> +# define __acquire_shared_cap(var)             do { } while (0)
> +# define __try_acquire_cap(var, ret)           (ret)
> +# define __try_acquire_shared_cap(var, ret)    (ret)
> +# define __release_cap(var)                    do { } while (0)
> +# define __release_shared_cap(var)             do { } while (0)
> +# define __assume_cap(var)                     do { (void)(var); } while (0)
> +# define __assume_shared_cap(var)              do { (void)(var); } while (0)
> +# define struct_with_capability(name, ...)     struct __VA_ARGS__ name
> +# define disable_capability_analysis()
> +# define enable_capability_analysis()
> +# define __no_capability_analysis
> +
> +#endif /* WARN_CAPABILITY_ANALYSIS */
> +
> +/**
> + * capability_unsafe() - disable capability checking for contained code
> + *
> + * Disables capability checking for contained statements or expression.
> + *
> + * .. code-block:: c
> + *
> + *     struct some_data {
> + *             spinlock_t lock;
> + *             int counter __guarded_by(&lock);
> + *     };
> + *
> + *     int foo(struct some_data *d)
> + *     {
> + *             // ...
> + *             // other code that is still checked ...
> + *             // ...
> + *             return capability_unsafe(d->counter);
> + *     }
> + */
> +#define capability_unsafe(...)         \
> +({                                     \
> +       disable_capability_analysis();  \
> +       __VA_ARGS__;                    \
> +       enable_capability_analysis()    \
> +})
> +
> +/**
> + * __capability_unsafe() - function attribute, disable capability checking
> + * @comment: comment explaining why opt-out is safe
> + *
> + * Function attribute denoting that capability analysis is disabled for the
> + * whole function. Forces adding an inline comment as argument.
> + */
> +#define __capability_unsafe(comment) __no_capability_analysis
> +
> +/**
> + * capability_unsafe_alias() - helper to insert a capability "alias barrier"
> + * @p: pointer aliasing a capability or object containing capabilities
> + *
> + * No-op function that acts as a "capability alias barrier", where the analysis
> + * rightfully detects that we're switching aliases, but the switch is considered
> + * safe but beyond the analysis reasoning abilities.
> + *
> + * This should be inserted before the first use of such an alias.
> + *
> + * Implementation Note: The compiler ignores aliases that may be reassigned but
> + * their value cannot be determined (e.g. when passing a non-const pointer to an
> + * alias as a function argument).
> + */
> +#define capability_unsafe_alias(p) _capability_unsafe_alias((void **)&(p))
> +static inline void _capability_unsafe_alias(void **p) { }
> +
> +/**
> + * token_capability() - declare an abstract global capability instance
> + * @name: token capability name
> + *
> + * Helper that declares an abstract global capability instance @name that can be
> + * used as a token capability, but not backed by a real data structure (linker
> + * error if accidentally referenced). The type name is `__capability_@name`.
> + */
> +#define token_capability(name, ...)                                    \
> +       struct_with_capability(__capability_##name, ##__VA_ARGS__) {};  \
> +       extern const struct __capability_##name *name
> +
> +/**
> + * token_capability_instance() - declare another instance of a global capability
> + * @cap: token capability previously declared with token_capability()
> + * @name: name of additional global capability instance
> + *
> + * Helper that declares an additional instance @name of the same token
> + * capability class @name. This is helpful where multiple related token
> + * capabilities are declared, as it also allows using the same underlying type
> + * (`__capability_@cap`) as function arguments.
> + */
> +#define token_capability_instance(cap, name)           \
> +       extern const struct __capability_##cap *name
> +
> +/*
> + * Common keywords for static capability analysis. Both Clang's capability
> + * analysis and Sparse's context tracking are currently supported.
> + */
>  #ifdef __CHECKER__
>
>  /* Sparse context/lock checking support. */
>  # define __must_hold(x)                __attribute__((context(x,1,1)))
> +# define __must_not_hold(x)
>  # define __acquires(x)         __attribute__((context(x,0,1)))
>  # define __cond_acquires(x)    __attribute__((context(x,0,-1)))
>  # define __releases(x)         __attribute__((context(x,1,0)))
>  # define __acquire(x)          __context__(x,1)
>  # define __release(x)          __context__(x,-1)
>  # define __cond_lock(x, c)     ((c) ? ({ __acquire(x); 1; }) : 0)
> +/* For Sparse, there's no distinction between exclusive and shared locks. */
> +# define __must_hold_shared    __must_hold
> +# define __acquires_shared     __acquires
> +# define __cond_acquires_shared __cond_acquires
> +# define __releases_shared     __releases
> +# define __acquire_shared      __acquire
> +# define __release_shared      __release
> +# define __cond_lock_shared    __cond_acquire
>
>  #else /* !__CHECKER__ */
>
> -# define __must_hold(x)
> -# define __acquires(x)
> -# define __cond_acquires(x)
> -# define __releases(x)
> -# define __acquire(x)          (void)0
> -# define __release(x)          (void)0
> -# define __cond_lock(x, c)     (c)
> +/**
> + * __must_hold() - function attribute, caller must hold exclusive capability
> + * @x: capability instance pointer
> + *
> + * Function attribute declaring that the caller must hold the given capability
> + * instance @x exclusively.
> + */
> +# define __must_hold(x)                __requires_cap(x)
> +
> +/**
> + * __must_not_hold() - function attribute, caller must not hold capability
> + * @x: capability instance pointer
> + *
> + * Function attribute declaring that the caller must not hold the given
> + * capability instance @x.
> + */
> +# define __must_not_hold(x)    __excludes_cap(x)
> +
> +/**
> + * __acquires() - function attribute, function acquires capability exclusively
> + * @x: capability instance pointer
> + *
> + * Function attribute declaring that the function acquires the given
> + * capability instance @x exclusively, but does not release it.
> + */
> +# define __acquires(x)         __acquires_cap(x)
> +
> +/**
> + * __cond_acquires() - function attribute, function conditionally
> + *                     acquires a capability exclusively
> + * @x: capability instance pointer
> + *
> + * Function attribute declaring that the function conditionally acquires the
> + * given capability instance @x exclusively, but does not release it.
> + */
> +# define __cond_acquires(x)    __try_acquires_cap(1, x)
> +
> +/**
> + * __releases() - function attribute, function releases a capability exclusively
> + * @x: capability instance pointer
> + *
> + * Function attribute declaring that the function releases the given capability
> + * instance @x exclusively. The capability must be held on entry.
> + */
> +# define __releases(x)         __releases_cap(x)
> +
> +/**
> + * __acquire() - function to acquire capability exclusively
> + * @x: capability instance pointer
> + *
> + * No-op function that acquires the given capability instance @x exclusively.
> + */
> +# define __acquire(x)          __acquire_cap(x)
> +
> +/**
> + * __release() - function to release capability exclusively
> + * @x: capability instance pointer
> + *
> + * No-op function that releases the given capability instance @x.
> + */
> +# define __release(x)          __release_cap(x)
> +
> +/**
> + * __cond_lock() - function that conditionally acquires a capability
> + *                 exclusively
> + * @x: capability instance pinter
> + * @c: boolean expression
> + *
> + * Return: result of @c
> + *
> + * No-op function that conditionally acquires capability instance @x
> + * exclusively, if the boolean expression @c is true. The result of @c is the
> + * return value, to be able to create a capability-enabled interface; for
> + * example:
> + *
> + * .. code-block:: c
> + *
> + *     #define spin_trylock(l) __cond_lock(&lock, _spin_trylock(&lock))
> + */
> +# define __cond_lock(x, c)     __try_acquire_cap(x, c)
> +
> +/**
> + * __must_hold_shared() - function attribute, caller must hold shared capability
> + * @x: capability instance pointer
> + *
> + * Function attribute declaring that the caller must hold the given capability
> + * instance @x with shared access.
> + */
> +# define __must_hold_shared(x) __requires_shared_cap(x)
> +
> +/**
> + * __acquires_shared() - function attribute, function acquires capability shared
> + * @x: capability instance pointer
> + *
> + * Function attribute declaring that the function acquires the given
> + * capability instance @x with shared access, but does not release it.
> + */
> +# define __acquires_shared(x)  __acquires_shared_cap(x)
> +
> +/**
> + * __cond_acquires_shared() - function attribute, function conditionally
> + *                            acquires a capability shared
> + * @x: capability instance pointer
> + *
> + * Function attribute declaring that the function conditionally acquires the
> + * given capability instance @x with shared access, but does not release it.
> + */
> +# define __cond_acquires_shared(x) __try_acquires_shared_cap(1, x)
> +
> +/**
> + * __releases_shared() - function attribute, function releases a
> + *                       capability shared
> + * @x: capability instance pointer
> + *
> + * Function attribute declaring that the function releases the given capability
> + * instance @x with shared access. The capability must be held on entry.
> + */
> +# define __releases_shared(x)  __releases_shared_cap(x)
> +
> +/**
> + * __acquire_shared() - function to acquire capability shared
> + * @x: capability instance pointer
> + *
> + * No-op function that acquires the given capability instance @x with shared
> + * access.
> + */
> +# define __acquire_shared(x)   __acquire_shared_cap(x)
> +
> +/**
> + * __release_shared() - function to release capability shared
> + * @x: capability instance pointer
> + *
> + * No-op function that releases the given capability instance @x with shared
> + * access.
> + */
> +# define __release_shared(x)   __release_shared_cap(x)
> +
> +/**
> + * __cond_lock_shared() - function that conditionally acquires a capability
> + *                        shared
> + * @x: capability instance pinter
> + * @c: boolean expression
> + *
> + * Return: result of @c
> + *
> + * No-op function that conditionally acquires capability instance @x with shared
> + * access, if the boolean expression @c is true. The result of @c is the return
> + * value, to be able to create a capability-enabled interface.
> + */
> +# define __cond_lock_shared(x, c) __try_acquire_shared_cap(x, c)
>
>  #endif /* __CHECKER__ */
>
> +/**
> + * __acquire_ret() - helper to acquire capability of return value
> + * @call: call expression
> + * @ret_expr: acquire expression that uses __ret
> + */
> +#define __acquire_ret(call, ret_expr)          \
> +       ({                                      \
> +               __auto_type __ret = call;       \
> +               __acquire(ret_expr);            \
> +               __ret;                          \
> +       })
> +
> +/**
> + * __acquire_shared_ret() - helper to acquire capability shared of return value
> + * @call: call expression
> + * @ret_expr: acquire shared expression that uses __ret
> + */
> +#define __acquire_shared_ret(call, ret_expr)   \
> +       ({                                      \
> +               __auto_type __ret = call;       \
> +               __acquire_shared(ret_expr);     \
> +               __ret;                          \
> +       })
> +
> +/*
> + * Attributes to mark functions returning acquired capabilities. This is purely
> + * cosmetic to help readability, and should be used with the above macros as
> + * follows:
> + *
> + *   struct foo { spinlock_t lock; ... };
> + *   ...
> + *   #define myfunc(...) __acquire_ret(_myfunc(__VA_ARGS__), &__ret->lock)
> + *   struct foo *_myfunc(int bar) __acquires_ret;
> + *   ...
> + */
> +#define __acquires_ret         __no_capability_analysis
> +#define __acquires_shared_ret  __no_capability_analysis
> +
>  #endif /* _LINUX_COMPILER_CAPABILITY_ANALYSIS_H */
> diff --git a/lib/Kconfig.debug b/lib/Kconfig.debug
> index dc0e0c6ed075..57e09615f88d 100644
> --- a/lib/Kconfig.debug
> +++ b/lib/Kconfig.debug
> @@ -613,6 +613,37 @@ config DEBUG_FORCE_WEAK_PER_CPU
>           To ensure that generic code follows the above rules, this
>           option forces all percpu variables to be defined as weak.
>
> +config WARN_CAPABILITY_ANALYSIS
> +       bool "Compiler capability-analysis warnings"
> +       depends on CC_IS_CLANG && CLANG_VERSION >= 220000
> +       # Branch profiling re-defines "if", which messes with the compiler's
> +       # ability to analyze __cond_acquires(..), resulting in false positives.
> +       depends on !TRACE_BRANCH_PROFILING

Err, wow! What and huh, and why? Crikes. I'm amazed you found such an
option exists. I must be very naive to have never heard of it and now
I wonder if it is needed and load bearing?

Ian


> +       default y
> +       help
> +         Capability analysis is a C language extension, which enables
> +         statically checking that user-definable "capabilities" are acquired
> +         and released where required.
> +
> +         Clang's name of the feature ("Thread Safety Analysis") refers to
> +         the original name of the feature; it was later expanded to be a
> +         generic "Capability Analysis" framework.
> +
> +         Requires Clang 22 or later.
> +
> +         Produces warnings by default. Select CONFIG_WERROR if you wish to
> +         turn these warnings into errors.
> +
> +config WARN_CAPABILITY_ANALYSIS_ALL
> +       bool "Enable capability analysis for all source files"
> +       depends on WARN_CAPABILITY_ANALYSIS
> +       depends on EXPERT && !COMPILE_TEST
> +       help
> +         Enable tree-wide capability analysis. This is likely to produce a
> +         large number of false positives - enable at your own risk.
> +
> +         If unsure, say N.
> +
>  endmenu # "Compiler options"
>
>  menu "Generic Kernel Debugging Instruments"
> diff --git a/scripts/Makefile.capability-analysis b/scripts/Makefile.capability-analysis
> new file mode 100644
> index 000000000000..e137751a4c9a
> --- /dev/null
> +++ b/scripts/Makefile.capability-analysis
> @@ -0,0 +1,7 @@
> +# SPDX-License-Identifier: GPL-2.0
> +
> +capability-analysis-cflags := -DWARN_CAPABILITY_ANALYSIS       \
> +       -fexperimental-late-parse-attributes -Wthread-safety    \
> +       -Wthread-safety-pointer -Wthread-safety-beta
> +
> +export CFLAGS_CAPABILITY_ANALYSIS := $(capability-analysis-cflags)
> diff --git a/scripts/Makefile.lib b/scripts/Makefile.lib
> index 1d581ba5df66..e0ac273bf9eb 100644
> --- a/scripts/Makefile.lib
> +++ b/scripts/Makefile.lib
> @@ -105,6 +105,16 @@ _c_flags += $(if $(patsubst n%,, \
>         -D__KCSAN_INSTRUMENT_BARRIERS__)
>  endif
>
> +#
> +# Enable capability analysis flags only where explicitly opted in.
> +# (depends on variables CAPABILITY_ANALYSIS_obj.o, CAPABILITY_ANALYSIS)
> +#
> +ifeq ($(CONFIG_WARN_CAPABILITY_ANALYSIS),y)
> +_c_flags += $(if $(patsubst n%,, \
> +               $(CAPABILITY_ANALYSIS_$(target-stem).o)$(CAPABILITY_ANALYSIS)$(if $(is-kernel-object),$(CONFIG_WARN_CAPABILITY_ANALYSIS_ALL))), \
> +               $(CFLAGS_CAPABILITY_ANALYSIS))
> +endif
> +
>  #
>  # Enable AutoFDO build flags except some files or directories we don't want to
>  # enable (depends on variables AUTOFDO_PROFILE_obj.o and AUTOFDO_PROFILE).
> --
> 2.51.0.384.g4c02a37b29-goog
>

^ permalink raw reply

* Re: [PATCH v3 00/35] Compiler-Based Capability- and Locking-Analysis
From: Linus Torvalds @ 2025-09-18 15:49 UTC (permalink / raw)
  To: Marco Elver
  Cc: Peter Zijlstra, Boqun Feng, Ingo Molnar, Will Deacon,
	David S. Miller, Luc Van Oostenryck, Paul E. McKenney,
	Alexander Potapenko, Arnd Bergmann, Bart Van Assche,
	Bill Wendling, Christoph Hellwig, Dmitry Vyukov, Eric Dumazet,
	Frederic Weisbecker, Greg Kroah-Hartman, Herbert Xu, Ian Rogers,
	Jann Horn, Joel Fernandes, Jonathan Corbet, Josh Triplett,
	Justin Stitt, Kees Cook, Kentaro Takeda, Lukas Bulwahn,
	Mark Rutland, Mathieu Desnoyers, Miguel Ojeda, Nathan Chancellor,
	Neeraj Upadhyay, Nick Desaulniers, Steven Rostedt, Tetsuo Handa,
	Thomas Gleixner, Thomas Graf, Uladzislau Rezki, Waiman Long,
	kasan-dev, linux-crypto, linux-doc, linux-kbuild, linux-kernel,
	linux-mm, linux-security-module, linux-sparse, llvm, rcu
In-Reply-To: <20250918140451.1289454-1-elver@google.com>

On Thu, 18 Sept 2025 at 07:05, Marco Elver <elver@google.com> wrote:
>
> Capability analysis is a C language extension, which enables statically
> checking that user-definable "capabilities" are acquired and released where
> required. An obvious application is lock-safety checking for the kernel's
> various synchronization primitives (each of which represents a "capability"),
> and checking that locking rules are not violated.
>
> Clang originally called the feature "Thread Safety Analysis" [1],

So this looks really interesting, but I absolutely *hate* the new
"capability" name.

We have existing and traditional - and very very different - meaning
of "capabilities" in the kernel, and having this thing called
"capability" is just wrong. Particularly as it then talks about
"acquiring capabilities" - which is *EXACTLY* what our lon-existing
capabilities are all about, but are something entirely and totally
different.

So please - call it something else. Even if clang then calls it
'capability analysis", within the context of a kernel, please ignore
that, and call it something that makes more sense (I don't think
"capabilities" make sense even in the context of clang, but hey,
that's _their_ choice - but we should not then take that bad choice
and run with it).

Sparse called it "context analysis", and while the "analysis" part is
debatable - sparse never did much anything clever enough to merit
calling it analysis - at least the "context" part of the name is I
think somewhat sane.

Because it's about making decisions based on the context the code runs in.

But I'm certainly not married to the "context" name either. I'd still
claim it makes more sense than "capability", but the real problem with
"capability" isn't that it doesn't make sense, it's that we already
*HAVE* that as a concept, and old and traditional use is important.

But we do use the word "context" in this context quite widely even
outside of the sparse usage, ie that's what we say when we talk about
things like locking and RCU (ie we talk about running in "process
context", or about "interrupt context" etc). That's obviously where
the sparse naming comes from - it's not like sparse made that up.

So I'm really happy to see compilers start exposing these kinds of
interfaces, and the patches look sane apart from the absolutely
horrible and unacceptable name. Really - there is no way in hell we
can call this "capability" in a kernel context.

I'd suggest just doing a search-and-replace of 's/capability/context/'
and it would already make things a ton better. But maybe there are
better names for this still?

I mean, even apart from the fact that we have an existing meaning for
"capability", just look at the documentation patch, and read the first
sentence:

  Capability analysis is a C language extension, which enables statically
  checking that user-definable "capabilities" are acquired and released where
  required.

and just from a plain English language standpoint, the word
"capability" makes zero sense. I think you even realized that, in that
you put that word in quotes, because it's _so_ nonsensical.

And if not "context", maybe some other word? But really, absolutely
*not* "capability". Because that's just crazy talk.

Please? Because other than this naming issue, I think this really is a
good idea.

           Linus

^ permalink raw reply

* Re: [PATCH v4 15/34] lsm: rename/rework ordered_lsm_parse() to lsm_order_parse()
From: Mimi Zohar @ 2025-09-18 15:55 UTC (permalink / raw)
  To: Paul Moore
  Cc: linux-security-module, linux-integrity, selinux, John Johansen,
	Roberto Sassu, Fan Wu, Mickaël Salaün,
	Günther Noack, Kees Cook, Micah Morton, Casey Schaufler,
	Tetsuo Handa, Nicolas Bouchinet, Xiu Jianfeng
In-Reply-To: <CAHC9VhSxookZfVQhDMdrikmFrk0VoUhEwMx0FR5DS3JfK_MY9Q@mail.gmail.com>

On Thu, 2025-09-18 at 11:38 -0400, Paul Moore wrote:
> > > +     /* Normal or "mutable" LSMs */
> 
> >   What are "mutable" LSMs?!

<snip>

> As the "or" would indicate, they are "normal" LSMs where the ordering
> is mutable, as opposed to the "first" or "last" LSMs.

Yes, of course I understand that.  Still would have been clearer adding the word
"Normal or "mutable" LSM ordering".

Mimi




^ permalink raw reply

* Re: [PATCH v4 20/34] lsm: cleanup the debug and console output in lsm_init.c
From: Paul Moore @ 2025-09-18 15:54 UTC (permalink / raw)
  To: Mimi Zohar
  Cc: linux-security-module, linux-integrity, selinux, John Johansen,
	Roberto Sassu, Fan Wu, Mickaël Salaün,
	Günther Noack, Kees Cook, Micah Morton, Casey Schaufler,
	Tetsuo Handa, Nicolas Bouchinet, Xiu Jianfeng
In-Reply-To: <4db3bb94c42f11240a880a439c7a678599d7053f.camel@linux.ibm.com>

On Thu, Sep 18, 2025 at 11:51 AM Mimi Zohar <zohar@linux.ibm.com> wrote:
> On Tue, 2025-09-16 at 18:03 -0400, Paul Moore wrote:
> > Move away from an init specific init_debug() macro to a more general
> > lsm_pr()/lsm_pr_cont()/lsm_pr_dbg() set of macros that are available
> > both before and after init.  In the process we do a number of minor
> > changes to improve the LSM initialization output and cleanup the code
> > somewhat.
> >
> > Reviewed-by: Casey Schaufler <casey@schaufler-ca.com>
> > Reviewed-by: John Johansen <john.johhansen@canonical.com>
> > Signed-off-by: Paul Moore <paul@paul-moore.com>
> > ---
> >  security/lsm.h      |  11 ++++
> >  security/lsm_init.c | 123 +++++++++++++++++++-------------------------
> >  security/security.c |   2 +
> >  3 files changed, 66 insertions(+), 70 deletions(-)
> >
> > diff --git a/security/lsm.h b/security/lsm.h
> > index dbe755c45e57..8dc267977ae0 100644
> > --- a/security/lsm.h
> > +++ b/security/lsm.h
> > @@ -6,9 +6,20 @@
> >  #ifndef _LSM_H_
> >  #define _LSM_H_
> >
> > +#include <linux/printk.h>
> >  #include <linux/lsm_hooks.h>
> >  #include <linux/lsm_count.h>
> >
> > +/* LSM debugging */
> > +extern bool lsm_debug;
> > +#define lsm_pr(...)          pr_info(__VA_ARGS__)
> > +#define lsm_pr_cont(...)     pr_cont(__VA_ARGS__)
> > +#define lsm_pr_dbg(...)
> >
> >                               \
> > +     do {                                                            \
> > +             if (lsm_debug)                                          \
> > +                     pr_info(__VA_ARGS__);                           \
> > +     } while (0)
>
> The existing pr_info and pr_cont themselves are #defines.  Is there a reason for
> these new "#define"?  If there is a valid reason for having these new defines,
> why aren't they simply prefixed with "lsm"?

With the creation of the lsm_pr_dbg() macro which gates the output on
the "lsm.debug" flag, I felt it was more consistent to name things
this way, even if lsm_pr() and lsm_pr_cont() are just effectively
renames of the existing pr_info() and pr_cont() symbols.

-- 
paul-moore.com

^ permalink raw reply

* Re: [PATCH v2 3/4] arch: copy_thread: pass clone_flags as u64
From: Paul Walmsley @ 2025-09-18 15:51 UTC (permalink / raw)
  To: Simon Schuster
  Cc: Dinh Nguyen, Christian Brauner, Arnd Bergmann, Andrew Morton,
	David Hildenbrand, Lorenzo Stoakes, Liam R. Howlett,
	Vlastimil Babka, Mike Rapoport, Suren Baghdasaryan, Michal Hocko,
	Ingo Molnar, Peter Zijlstra, Juri Lelli, Vincent Guittot,
	Dietmar Eggemann, Steven Rostedt, Ben Segall, Mel Gorman,
	Valentin Schneider, Kees Cook, Paul Walmsley, Palmer Dabbelt,
	Albert Ou, Alexandre Ghiti, Guo Ren, Oleg Nesterov, Jens Axboe,
	Alexander Viro, Jan Kara, Tejun Heo, Johannes Weiner,
	Michal Koutný, Paul Moore, Serge Hallyn, James Morris,
	Serge E. Hallyn, Anna-Maria Behnsen, Frederic Weisbecker,
	Thomas Gleixner, Masami Hiramatsu, David S. Miller, Eric Dumazet,
	Jakub Kicinski, Paolo Abeni, Simon Horman, Mathieu Desnoyers,
	Arnaldo Carvalho de Melo, Namhyung Kim, Mark Rutland,
	Alexander Shishkin, Jiri Olsa, Ian Rogers, Adrian Hunter,
	John Johansen, Stephen Smalley, Ondrej Mosnacek, Kentaro Takeda,
	Tetsuo Handa, Richard Henderson, Matt Turner, Vineet Gupta,
	Russell King, Catalin Marinas, Will Deacon, Brian Cain,
	Huacai Chen, WANG Xuerui, Geert Uytterhoeven, Michal Simek,
	Thomas Bogendoerfer, Jonas Bonn, Stefan Kristiansson,
	Stafford Horne, James E.J. Bottomley, Helge Deller,
	Madhavan Srinivasan, Michael Ellerman, Nicholas Piggin,
	Christophe Leroy, Heiko Carstens, Vasily Gorbik,
	Alexander Gordeev, Christian Borntraeger, Sven Schnelle,
	Yoshinori Sato, Rich Felker, John Paul Adrian Glaubitz,
	Andreas Larsson, Richard Weinberger, Anton Ivanov, Johannes Berg,
	Borislav Petkov, Dave Hansen, x86, H. Peter Anvin, Chris Zankel,
	Max Filippov, linux-mm, linux-kernel, linux-riscv, linux-csky,
	linux-block, linux-fsdevel, cgroups, linux-security-module,
	linux-trace-kernel, netdev, linux-perf-users, apparmor, selinux,
	linux-alpha, linux-snps-arc, linux-arm-kernel, linux-hexagon,
	loongarch, linux-m68k, linux-mips, linux-openrisc, linux-parisc,
	linuxppc-dev, linux-s390, linux-sh, sparclinux, linux-um
In-Reply-To: <20250901-nios2-implement-clone3-v2-3-53fcf5577d57@siemens-energy.com>

On Mon, 1 Sep 2025, Simon Schuster via B4 Relay wrote:

> From: Simon Schuster <schuster.simon@siemens-energy.com>
> 
> With the introduction of clone3 in commit 7f192e3cd316 ("fork: add
> clone3") the effective bit width of clone_flags on all architectures was
> increased from 32-bit to 64-bit, with a new type of u64 for the flags.
> However, for most consumers of clone_flags the interface was not
> changed from the previous type of unsigned long.
> 
> While this works fine as long as none of the new 64-bit flag bits
> (CLONE_CLEAR_SIGHAND and CLONE_INTO_CGROUP) are evaluated, this is still
> undesirable in terms of the principle of least surprise.
> 
> Thus, this commit fixes all relevant interfaces of the copy_thread
> function that is called from copy_process to consistently pass
> clone_flags as u64, so that no truncation to 32-bit integers occurs on
> 32-bit architectures.
> 
> Signed-off-by: Simon Schuster <schuster.simon@siemens-energy.com>

Acked-by: Paul Walmsley <pjw@kernel.org> # for RISC-V

Thanks!


- Paul

^ permalink raw reply

* Re: [PATCH v4 20/34] lsm: cleanup the debug and console output in lsm_init.c
From: Mimi Zohar @ 2025-09-18 15:50 UTC (permalink / raw)
  To: Paul Moore, linux-security-module, linux-integrity, selinux
  Cc: John Johansen, Roberto Sassu, Fan Wu, Mickaël Salaün,
	Günther Noack, Kees Cook, Micah Morton, Casey Schaufler,
	Tetsuo Handa, Nicolas Bouchinet, Xiu Jianfeng
In-Reply-To: <20250916220355.252592-56-paul@paul-moore.com>

On Tue, 2025-09-16 at 18:03 -0400, Paul Moore wrote:
> Move away from an init specific init_debug() macro to a more general
> lsm_pr()/lsm_pr_cont()/lsm_pr_dbg() set of macros that are available
> both before and after init.  In the process we do a number of minor
> changes to improve the LSM initialization output and cleanup the code
> somewhat.
> 
> Reviewed-by: Casey Schaufler <casey@schaufler-ca.com>
> Reviewed-by: John Johansen <john.johhansen@canonical.com>
> Signed-off-by: Paul Moore <paul@paul-moore.com>
> ---
>  security/lsm.h      |  11 ++++
>  security/lsm_init.c | 123 +++++++++++++++++++-------------------------
>  security/security.c |   2 +
>  3 files changed, 66 insertions(+), 70 deletions(-)
> 
> diff --git a/security/lsm.h b/security/lsm.h
> index dbe755c45e57..8dc267977ae0 100644
> --- a/security/lsm.h
> +++ b/security/lsm.h
> @@ -6,9 +6,20 @@
>  #ifndef _LSM_H_
>  #define _LSM_H_
>  
> +#include <linux/printk.h>
>  #include <linux/lsm_hooks.h>
>  #include <linux/lsm_count.h>
>  
> +/* LSM debugging */
> +extern bool lsm_debug;
> +#define lsm_pr(...)		pr_info(__VA_ARGS__)
> +#define lsm_pr_cont(...)	pr_cont(__VA_ARGS__)
> +#define lsm_pr_dbg(...)
> 
> 				\
> +	do {								\
> +		if (lsm_debug)						\
> +			pr_info(__VA_ARGS__);				\
> +	} while (0)


The existing pr_info and pr_cont themselves are #defines.  Is there a reason for
these new "#define"?  If there is a valid reason for having these new defines,
why aren't they simply prefixed with "lsm"?

Mimi

^ permalink raw reply

* Re: [PATCH v4 15/34] lsm: rename/rework ordered_lsm_parse() to lsm_order_parse()
From: Paul Moore @ 2025-09-18 15:38 UTC (permalink / raw)
  To: Mimi Zohar
  Cc: linux-security-module, linux-integrity, selinux, John Johansen,
	Roberto Sassu, Fan Wu, Mickaël Salaün,
	Günther Noack, Kees Cook, Micah Morton, Casey Schaufler,
	Tetsuo Handa, Nicolas Bouchinet, Xiu Jianfeng
In-Reply-To: <8b560b9522c1c42e26a108e2f9b2977901d73649.camel@linux.ibm.com>

On Thu, Sep 18, 2025 at 7:30 AM Mimi Zohar <zohar@linux.ibm.com> wrote:
> On Tue, 2025-09-16 at 18:03 -0400, Paul Moore wrote:
> > Rename ordered_lsm_parse() to lsm_order_parse() for the sake of
> > consistency with the other LSM initialization routines, and also
> > do some minor rework of the function.  Aside from some minor style
> > decisions, the majority of the rework involved shuffling the order
> > of the LSM_FLAG_LEGACY and LSM_ORDER_FIRST code so that the
> > LSM_FLAG_LEGACY checks are handled first; it is important to note
> > that this doesn't affect the order in which the LSMs are registered.
> >
> > Reviewed-by: Casey Schaufler <casey@schaufler-ca.com>
> > Reviewed-by: John Johansen <john.johhansen@canonical.com>
> > Signed-off-by: Paul Moore <paul@paul-moore.com>
> > ---
> >  security/lsm_init.c | 82 ++++++++++++++++++++-------------------------
> >  1 file changed, 37 insertions(+), 45 deletions(-)
> >
> > diff --git a/security/lsm_init.c b/security/lsm_init.c
> > index a314484d7c2f..7b2491120fc8 100644
> > --- a/security/lsm_init.c
> > +++ b/security/lsm_init.c
> > @@ -228,83 +228,75 @@ static void __init initialize_lsm(struct lsm_info *lsm)
> >       }
> >  }
> >
> > -/* Populate ordered LSMs list from comma-separated LSM name list. */
> > -static void __init ordered_lsm_parse(const char *order, const char *origin)
> > +/**
> > + * lsm_order_parse - Parse the comma delimited LSM list
> > + * @list: LSM list
> > + * @src: source of the list
> > + */
> > +static void __init lsm_order_parse(const char *list, const char *src)
> >  {
> >       struct lsm_info *lsm;
> >       char *sep, *name, *next;
> >
> > -     /* LSM_ORDER_FIRST is always first. */
> > -     lsm_for_each_raw(lsm) {
> > -             if (lsm->order == LSM_ORDER_FIRST)
> > -                     lsm_order_append(lsm, "  first");
> > -     }
> > -
> > -     /* Process "security=", if given. */
> > +     /* Handle any Legacy LSM exclusions if one was specified. */
> >       if (lsm_order_legacy) {
> > -             struct lsm_info *major;
> > -
> >               /*
> > -              * To match the original "security=" behavior, this
> > -              * explicitly does NOT fallback to another Legacy Major
> > -              * if the selected one was separately disabled: disable
> > -              * all non-matching Legacy Major LSMs.
> > +              * To match the original "security=" behavior, this explicitly
> > +              * does NOT fallback to another Legacy Major if the selected
> > +              * one was separately disabled: disable all non-matching
> > +              * Legacy Major LSMs.
> >                */
> > -             lsm_for_each_raw(major) {
> > -                     if ((major->flags & LSM_FLAG_LEGACY_MAJOR) &&
> > -                         strcmp(major->id->name, lsm_order_legacy) != 0) {
> > -                             lsm_enabled_set(major, false);
> > +             lsm_for_each_raw(lsm) {
> > +                     if ((lsm->flags & LSM_FLAG_LEGACY_MAJOR) &&
> > +                          strcmp(lsm->id->name, lsm_order_legacy)) {
> > +                             lsm_enabled_set(lsm, false);
> >                               init_debug("security=%s disabled: %s (only one legacy major LSM)\n",
> > -                                        lsm_order_legacy, major->id->name);
> > +                                        lsm_order_legacy, lsm->id->name);
> >                       }
> >               }
> >       }
> >
> > -     sep = kstrdup(order, GFP_KERNEL);
> > +     /* LSM_ORDER_FIRST */
> > +     lsm_for_each_raw(lsm) {
> > +             if (lsm->order == LSM_ORDER_FIRST)
> > +                     lsm_order_append(lsm, "first");
> > +     }
> > +
> > +     /* Normal or "mutable" LSMs */
>
> Paul, there's a reason for another set of eyes reviewing patches and yes, even,
> comments.

Mimi, if you've read my emails over the years, or discussed this topic
with me in person or off-list, you'll know that I almost always* hold
my patches to the same standard as any other contributor; even in
cases where I know there is likely to be no review beyond my own.

[*] There have been exceptions in the past due to build breakages and
other "critical" fixes that need to be merged ASAP, but even then the
patches are posted and subject to review and additional
patching/fixing afterwards.

>  What are "mutable" LSMs?!

As the "or" would indicate, they are "normal" LSMs where the ordering
is mutable, as opposed to the "first" or "last" LSMs.

-- 
paul-moore.com

^ permalink raw reply

* Re: [PATCH v4 17/34] lsm: cleanup initialize_lsm() and rename to lsm_init_single()
From: Mimi Zohar @ 2025-09-18 15:28 UTC (permalink / raw)
  To: Paul Moore, linux-security-module, linux-integrity, selinux
  Cc: John Johansen, Roberto Sassu, Fan Wu, Mickaël Salaün,
	Günther Noack, Kees Cook, Micah Morton, Casey Schaufler,
	Tetsuo Handa, Nicolas Bouchinet, Xiu Jianfeng
In-Reply-To: <20250916220355.252592-53-paul@paul-moore.com>

On Tue, 2025-09-16 at 18:03 -0400, Paul Moore wrote:
> Rename initialize_lsm() to be more consistent with the rest of the LSM
> initialization changes and rework the function itself to better fit
> with the "exit on fail" coding pattern.
> 
> Reviewed-by: Kees Cook <kees@kernel.org>
> Reviewed-by: John Johansen <john.johansen@canonical.com>
> Reviewed-by: Casey Schaufler <casey@schaufler-ca.com>
> Signed-off-by: Paul Moore <paul@paul-moore.com>

Reviewed-by: Mimi Zohar <zohar@linux.ibm.com>

^ permalink raw reply

* Re: [PATCH v4 24/34] loadpin: move initcalls to the LSM framework
From: Paul Moore @ 2025-09-18 15:27 UTC (permalink / raw)
  To: Mimi Zohar
  Cc: linux-security-module, linux-integrity, selinux, John Johansen,
	Roberto Sassu, Fan Wu, Mickaël Salaün,
	Günther Noack, Kees Cook, Micah Morton, Casey Schaufler,
	Tetsuo Handa, Nicolas Bouchinet, Xiu Jianfeng
In-Reply-To: <598e9ed199ba23e7e11c5ea29132bd2e3202305f.camel@linux.ibm.com>

On Thu, Sep 18, 2025 at 7:16 AM Mimi Zohar <zohar@linux.ibm.com> wrote:
> On Tue, 2025-09-16 at 18:03 -0400, Paul Moore wrote:
> > Acked-by: Kees Cook <kees@kernel.org>
> > Reviewed-by: John Johansen <john.johhansen@canonical.com>
> > Signed-off-by: Paul Moore <paul@paul-moore.com>
>
> For the first couple of iterations, the patch descriptions needed to be added or
> improved upon.

As was discussed in the first cover letter, and in the related
reviews, the first iteration was simply a FYI primarily for the sake
of Casey who was working on a patchset which has some overlap.

> Some of the patch descriptions are still missing (e.g. 25, 27,
> etc).  Is this intentional because you feel it is redundant ...

Yes.  Take this particular patch as an example, the conversion to
using the new initcall mechanism for Loadpin is perhaps one of the
more trivial patches one might see in the kernel, the subject line of
"loadpin: move initcalls to the LSM framework" is sufficient to
document the patch as far as I'm concerned.

> FYI, teaching newbies how to break up a patch set is not easy.  This patch set
> is nicely broken up and would be a good example.  However, leaving out the patch
> description would be teaching the wrong thing.

Documentation is a good and important part of the work we do, but
redundant and/or excessive documentation simply for the sake of
satisfying a checkbox is not a good thing IMO.

-- 
paul-moore.com

^ permalink raw reply

* Re: [PATCH v4 16/34] lsm: cleanup the LSM blob size code
From: Mimi Zohar @ 2025-09-18 15:14 UTC (permalink / raw)
  To: Paul Moore, linux-security-module, linux-integrity, selinux
  Cc: John Johansen, Roberto Sassu, Fan Wu, Mickaël Salaün,
	Günther Noack, Kees Cook, Micah Morton, Casey Schaufler,
	Tetsuo Handa, Nicolas Bouchinet, Xiu Jianfeng
In-Reply-To: <20250916220355.252592-52-paul@paul-moore.com>

On Tue, 2025-09-16 at 18:03 -0400, Paul Moore wrote:
> Convert the lsm_blob_size fields to unsigned integers as there is no
> current need for them to be negative, change "lsm_set_blob_size()" to
> "lsm_blob_size_update()" to better reflect reality, and perform some
> other minor cleanups to the associated code.
> 
> Reviewed-by: Kees Cook <kees@kernel.org>
> Reviewed-by: John Johansen <john.johansen@canonical.com>
> Reviewed-by: Casey Schaufler <casey@schaufler-ca.com>
> Signed-off-by: Paul Moore <paul@paul-moore.com>

Reviewed-by: Mimi Zohar <zohar@linux.ibm.com>


^ permalink raw reply

* Re: [PATCH v3 00/35] Compiler-Based Capability- and Locking-Analysis
From: Christoph Hellwig @ 2025-09-18 14:38 UTC (permalink / raw)
  To: Marco Elver
  Cc: Christoph Hellwig, Peter Zijlstra, Boqun Feng, Ingo Molnar,
	Will Deacon, David S. Miller, Luc Van Oostenryck,
	Paul E. McKenney, Alexander Potapenko, Arnd Bergmann,
	Bart Van Assche, Bill Wendling, Dmitry Vyukov, Eric Dumazet,
	Frederic Weisbecker, Greg Kroah-Hartman, Herbert Xu, Ian Rogers,
	Jann Horn, Joel Fernandes, Jonathan Corbet, Josh Triplett,
	Justin Stitt, Kees Cook, Kentaro Takeda, Lukas Bulwahn,
	Mark Rutland, Mathieu Desnoyers, Miguel Ojeda, Nathan Chancellor,
	Neeraj Upadhyay, Nick Desaulniers, Steven Rostedt, Tetsuo Handa,
	Thomas Gleixner, Thomas Graf, Uladzislau Rezki, Waiman Long,
	kasan-dev, linux-crypto, linux-doc, linux-kbuild, linux-kernel,
	linux-mm, linux-security-module, linux-sparse, llvm, rcu
In-Reply-To: <CANpmjNN8Vx5p+0xZAjHA4s6HaGaEdMf_u1c1jiOf=ZKqYYz9Nw@mail.gmail.com>

On Thu, Sep 18, 2025 at 04:30:55PM +0200, Marco Elver wrote:
> Not officially, but I can try to build something to share if you prefer.
> Or a script that automatically pulls and builds clang for you - I have
> this old script I just updated to the above commit:
> https://gist.github.com/melver/fe8a5fd9e43e21fab569ee24fc9c6072
> Does that help?

Just kicked it off, I'll see how long this will take on my laptop.
At least the error checking that tells me about dependencies and work
that needs to be done before starting the build is nice so that I
hopefully don't have to restart the build too often.

^ permalink raw reply

* Re: [PATCH RFC 2/4] landlock: implement memfd detection
From: Mickaël Salaün @ 2025-09-18 14:26 UTC (permalink / raw)
  To: Abhinav Saxena, Paul Moore
  Cc: Fan Wu, Günther Noack, James Morris, Serge E. Hallyn,
	Shuah Khan, Nathan Chancellor, Nick Desaulniers, Bill Wendling,
	Justin Stitt, linux-security-module, linux-kernel,
	linux-kselftest, llvm, Daniel Verkamp, Jeff Xu
In-Reply-To: <20250918.kieghied1ahS@digikod.net>

On Thu, Sep 18, 2025 at 04:07:48PM +0200, Mickaël Salaün wrote:
> On Tue, Jul 22, 2025 at 03:56:38PM -0600, Abhinav Saxena wrote:
> > Fan Wu <wufan@kernel.org> writes:
> > 
> > > On Sat, Jul 19, 2025 at 4:13 AM Abhinav Saxena <xandfury@gmail.com> wrote:
> > >>
> > >> Add is_memfd_file() function to reliably detect memfd files by checking
> > >> for “memfd:” prefix in dentry names on shmem-backed files. This
> > >> distinguishes true memfd files from regular shmem files.
> > >>
> > >> Move domain_is_scoped() to domain.c for reuse across subsystems.
> > >> Add comprehensive kunit tests for memfd detection edge cases.
> > >>
> > >> Signed-off-by: Abhinav Saxena <xandfury@gmail.com>
> > >> —
> > >>  security/landlock/domain.c |  67 +++++++++++++++
> > >>  security/landlock/domain.h |   4 +
> > >>  security/landlock/fs.c     | 210 +++++++++++++++++++++++++++++++++++++++++++++
> > >>  security/landlock/task.c   |  67 —————
> > >>  4 files changed, 281 insertions(+), 67 deletions(-)
> > >
> > > …
> > >
> > >>
> > >> +/**
> > >> + * is_memfd_file - Check if file was created via memfd_create()
> > >> + * @file: File to check
> > >> + *
> > >> + * Returns true if @file was created via memfd_create(), false otherwise.
> > >> + *
> > >> + * memfd files are shmem-backed files with “memfd:” prefix in their dentry name.
> > >> + * This is the definitive way to distinguish memfd files from regular shmem
> > >> + * files.
> > >> + */
> > >> +static bool is_memfd_file(struct file *file)
> > >> +{
> > >> +       const struct dentry *dentry;
> > >> +       const unsigned char *name;
> > >> +       size_t name_len;
> > >> +
> > >> +       /* Fast path: basic validation */
> > >> +       if (unlikely(!file))
> > >> +               return false;
> > >> +
> > >> +       /* Must be shmem-backed first - this is the cheapest definitive check */
> > >> +       if (!shmem_file(file))
> > >> +               return false;
> 
> In which case a memfd would not be detected by this function?
> shmem_file_operations or hugetlbfs_file_operations are always set for
> memfd right?
> 
> We could export memfd_get_seals() to stay consistent over time.
> 
> > >> +
> > >> +#ifdef CONFIG_MEMFD_CREATE
> > >> +
> > >> +       /* Validate dentry and get name info */
> > >> +       dentry = file->f_path.dentry;
> > >> +       if (unlikely(!dentry))
> > >> +               return false;
> > >> +
> > >> +       name_len = dentry->d_name.len;
> > >> +       name = dentry->d_name.name;
> > >> +
> > >> +       /* memfd files always have “memfd:” prefix (6 characters) */
> > >> +       if (name_len < 6 || unlikely(!name))
> > >> +               return false;
> > >> +
> > >> +       /* Check for exact “memfd:” prefix */
> > >> +       return memcmp(name, “memfd:”, 6) == 0;
> > >> +#else
> > >> +       return false;
> > >> +#endif
> > >
> > > I was trying to do something similar early this year but didn’t hear
> > > feedback from the linux-mm folks.
> > > <https://lore.kernel.org/linux-security-module/20250129203932.22165-1-wufan@kernel.org/>
> > >
> > > I have considered this approach but didn’t use it. My concern is,
> > > potentially a malicious user can create a file in a shmem fs, e.g.
> > > tmpfs , with the “memfd:” prefix, which can be used to bypass security
> > > policy.
> 
> In the case of Landlock that should not be a security issue but a
> compatibility issue, which is not better.
> 
> > > (Resending this message due to a misconfiguration with my email
> > > client. Apologies for any inconvenience.)
> > >
> > > -Fan
> > 
> > Hi Fan,
> > 
> > Thanks for your comments.
> > 
> > I agree that an LSM hook into memfd_create() would be a much better
> > solution. In the absence of such a function, do you think adding a

Hmm, we cannot use hook_file_alloc_security() to enforce restriction on
memfd creation because the inode mode is set after this call by
memfd.c:alloc_file().  So we should indeed add a new LSM hook.  That
would also improve security checks on file creation for other file
types.

> > `d_unlinked(dentry)` check could serve as an additional verification?
> > 
> > I say things since I *think* that legitimate memfd files are always
> > unlinked while spoofed tmpfs files remain linked. I could be wrong
> > though.
> 
> We should just use the same checks used by the memfd-related
> syscalls/operations to detect such file.
> 
> > 
> > In any case, we can test this approach using kprobes to validate
> > the behavior.
> > 
> > -Abhinav
> 

^ permalink raw reply

* Re: [PATCH v3 00/35] Compiler-Based Capability- and Locking-Analysis
From: Marco Elver @ 2025-09-18 14:30 UTC (permalink / raw)
  To: Christoph Hellwig
  Cc: Peter Zijlstra, Boqun Feng, Ingo Molnar, Will Deacon,
	David S. Miller, Luc Van Oostenryck, Paul E. McKenney,
	Alexander Potapenko, Arnd Bergmann, Bart Van Assche,
	Bill Wendling, Dmitry Vyukov, Eric Dumazet, Frederic Weisbecker,
	Greg Kroah-Hartman, Herbert Xu, Ian Rogers, Jann Horn,
	Joel Fernandes, Jonathan Corbet, Josh Triplett, Justin Stitt,
	Kees Cook, Kentaro Takeda, Lukas Bulwahn, Mark Rutland,
	Mathieu Desnoyers, Miguel Ojeda, Nathan Chancellor,
	Neeraj Upadhyay, Nick Desaulniers, Steven Rostedt, Tetsuo Handa,
	Thomas Gleixner, Thomas Graf, Uladzislau Rezki, Waiman Long,
	kasan-dev, linux-crypto, linux-doc, linux-kbuild, linux-kernel,
	linux-mm, linux-security-module, linux-sparse, llvm, rcu
In-Reply-To: <20250918141511.GA30263@lst.de>

On Thu, 18 Sept 2025 at 16:15, Christoph Hellwig <hch@lst.de> wrote:
>
> On Thu, Sep 18, 2025 at 03:59:11PM +0200, Marco Elver wrote:
> > A Clang version that supports `-Wthread-safety-pointer` and the new
> > alias-analysis of capability pointers is required (from this version
> > onwards):
> >
> >       https://github.com/llvm/llvm-project/commit/b4c98fcbe1504841203e610c351a3227f36c92a4 [3]
>
> There's no chance to make say x86 pre-built binaries for that available?

Not officially, but I can try to build something to share if you prefer.
Or a script that automatically pulls and builds clang for you - I have
this old script I just updated to the above commit:
https://gist.github.com/melver/fe8a5fd9e43e21fab569ee24fc9c6072
Does that help?

^ permalink raw reply

* Re: [PATCH v3 00/35] Compiler-Based Capability- and Locking-Analysis
From: Christoph Hellwig @ 2025-09-18 14:15 UTC (permalink / raw)
  To: Marco Elver
  Cc: Peter Zijlstra, Boqun Feng, Ingo Molnar, Will Deacon,
	David S. Miller, Luc Van Oostenryck, Paul E. McKenney,
	Alexander Potapenko, Arnd Bergmann, Bart Van Assche,
	Bill Wendling, Christoph Hellwig, Dmitry Vyukov, Eric Dumazet,
	Frederic Weisbecker, Greg Kroah-Hartman, Herbert Xu, Ian Rogers,
	Jann Horn, Joel Fernandes, Jonathan Corbet, Josh Triplett,
	Justin Stitt, Kees Cook, Kentaro Takeda, Lukas Bulwahn,
	Mark Rutland, Mathieu Desnoyers, Miguel Ojeda, Nathan Chancellor,
	Neeraj Upadhyay, Nick Desaulniers, Steven Rostedt, Tetsuo Handa,
	Thomas Gleixner, Thomas Graf, Uladzislau Rezki, Waiman Long,
	kasan-dev, linux-crypto, linux-doc, linux-kbuild, linux-kernel,
	linux-mm, linux-security-module, linux-sparse, llvm, rcu
In-Reply-To: <20250918140451.1289454-1-elver@google.com>

On Thu, Sep 18, 2025 at 03:59:11PM +0200, Marco Elver wrote:
> A Clang version that supports `-Wthread-safety-pointer` and the new
> alias-analysis of capability pointers is required (from this version
> onwards):
> 
> 	https://github.com/llvm/llvm-project/commit/b4c98fcbe1504841203e610c351a3227f36c92a4 [3]

There's no chance to make say x86 pre-built binaries for that available?


^ permalink raw reply

* Re: [PATCH RFC 0/4] landlock: add LANDLOCK_SCOPE_MEMFD_EXEC execution
From: Mickaël Salaün @ 2025-09-18 14:07 UTC (permalink / raw)
  To: Abhinav Saxena
  Cc: Günther Noack, Paul Moore, James Morris, Serge E. Hallyn,
	Shuah Khan, Nathan Chancellor, Nick Desaulniers, Bill Wendling,
	Justin Stitt, linux-security-module, linux-kernel,
	linux-kselftest, llvm, Daniel Verkamp, Jeff Xu
In-Reply-To: <20250719-memfd-exec-v1-0-0ef7feba5821@gmail.com>

Thanks for this patch series Abhinav!  The code looks good overall, but
we should clarify the design.  Sorry for the delayed response, it is on
my radar now.

CCing Jeff and Daniel

On Sat, Jul 19, 2025 at 05:13:10AM -0600, Abhinav Saxena wrote:
> This patch series introduces LANDLOCK_SCOPE_MEMFD_EXEC, a new Landlock
> scoping mechanism that restricts execution of anonymous memory file
> descriptors (memfd) created via memfd_create(2). This addresses security
> gaps where processes can bypass W^X policies and execute arbitrary code
> through anonymous memory objects.
> 
> Fixes: https://github.com/landlock-lsm/linux/issues/37
> 
> SECURITY PROBLEM
> ================
> 
> Current Landlock filesystem restrictions do not cover memfd objects,
> allowing processes to:
> 
> 1. Read-to-execute bypass: Create writable memfd, inject code,
>    then execute via mmap(PROT_EXEC) or direct execve()
> 2. Anonymous execution: Execute code without touching the filesystem via
>    execve("/proc/self/fd/N") where N is a memfd descriptor

> 3. Cross-domain access violations: Pass memfd between processes to
>    bypass domain restrictions

Landlock only restricts access at open time, which is a useful property.
This enables to create more restricted sandboxes but still get access to
outside resources via trusted processes.  If the process passing the FDs
is not trusted, the sandboxed process could just ask to execute
arbitrary code outside the sandbox anyway.

However, the Landlock scopes are designed to block IPC from within a
sandbox to outside the sandbox.  We could have a new scope to forbid a
sandbox process to receive or inherit file descriptors, but that would
be a different and generic feature.  For compatibility reasons, this
might not be easy to implement and I think there are more important
features to implement before that.

Thinking more about it, restricting memfd should not be a "scoped" flag
because the semantic is not the same, but we should have a new ruleset
property instead, something like "ruleset.denied" with a related
LANDLOCK_DENY_EXECUTE_MEMFD flag.  This flag will only have an impact on
newly created memfd from a sandboxed process with this restriction at
creation time. This could be implemented with hook_file_alloc_security()
by checking if the file is indeed a memfd and checking inode->i_mode for
executability bits (which would imply MFD_NOEXEC_SEAL).

> 
> These scenarios can occur in sandboxed environments where filesystem
> access is restricted but memfd creation remains possible.
> 
> IMPLEMENTATION
> ==============
> 
> The implementation adds hierarchical execution control through domain
> scoping:
> 
> Core Components:
> - is_memfd_file(): Reliable memfd detection via "memfd:" dentry prefix
> - domain_is_scoped(): Cross-domain hierarchy checking (moved to domain.c)
> - LSM hooks: mmap_file, file_mprotect, bprm_creds_for_exec
> - Creation-time restrictions: hook_file_alloc_security
> 
> Security Matrix:
> Execution decisions follow domain hierarchy rules preventing both
> same-domain bypass attempts and cross-domain access violations while
> preserving legitimate hierarchical access patterns.
> 
> Domain Hierarchy with LANDLOCK_SCOPE_MEMFD_EXEC:
> ===============================================
> 
> Root (no domain) - No restrictions
>   |
>   +-- Domain A [SCOPE_MEMFD_EXEC] Layer 1
>   |     +-- memfd_A (tagged with Domain A as creator)
>   |     |
>   |     +-- Domain A1 (child) [NO SCOPE] Layer 2
>   |     |     +-- Inherits Layer 1 restrictions from parent
>   |     |     +-- memfd_A1 (can create, inherits restrictions)
>   |     |     +-- Domain A1a [SCOPE_MEMFD_EXEC] Layer 3
>   |     |           +-- memfd_A1a (tagged with Domain A1a)
>   |     |
>   |     +-- Domain A2 (child) [SCOPE_MEMFD_EXEC] Layer 2
>   |           +-- memfd_A2 (tagged with Domain A2 as creator)
>   |           +-- CANNOT access memfd_A1 (different subtree)
>   |
>   +-- Domain B [SCOPE_MEMFD_EXEC] Layer 1
>         +-- memfd_B (tagged with Domain B as creator)
>         +-- CANNOT access ANY memfd from Domain A subtree
> 
> Execution Decision Matrix:
> ========================
> Executor->  |  A  | A1 | A1a | A2 | B  | Root
> Creator     |     |    |     |    |    |
> ------------|-----|----|-----|----|----|-----
> Domain A    |  X  | X  | X   | X  | X  |  Y
> Domain A1   |  Y  | X  | X   | X  | X  |  Y
> Domain A1a  |  Y  | Y  | X   | X  | X  |  Y
> Domain A2   |  Y  | X  | X   | X  | X  |  Y
> Domain B    |  X  | X  | X   | X  | X  |  Y
> Root        |  Y  | Y  | Y   | Y  | Y  |  Y
> 
> Legend: Y = Execution allowed, X = Execution denied

Because checks should not be related to scopes, this will be much
simpler.

> 
> Scenarios Covered:
> - Direct mmap(PROT_EXEC) on memfd files
> - Two-stage mmap(PROT_READ) + mprotect(PROT_EXEC) bypass attempts
> - execve("/proc/self/fd/N") anonymous execution
> - execveat() and fexecve() file descriptor execution
> - Cross-process memfd inheritance and IPC passing
> 
> TESTING
> =======
> 
> All patches have been validated with:
> - scripts/checkpatch.pl --strict (clean)
> - Selftests covering same-domain restrictions, cross-domain 
>   hierarchy enforcement, and regular file isolation
> - KUnit tests for memfd detection edge cases

Thanks for all these tests!

> 
> DISCLAIMER
> ==========
> 
> My understanding of Landlock scoping semantics may be limited, but this
> implementation reflects my current understanding based on available
> documentation and code analysis. I welcome feedback and corrections
> regarding the scoping logic and domain hierarchy enforcement.
> 
> Signed-off-by: Abhinav Saxena <xandfury@gmail.com>
> ---
> Abhinav Saxena (4):
>       landlock: add LANDLOCK_SCOPE_MEMFD_EXEC scope
>       landlock: implement memfd detection
>       landlock: add memfd exec LSM hooks and scoping
>       selftests/landlock: add memfd execution tests
> 
>  include/uapi/linux/landlock.h                      |   5 +
>  security/landlock/.kunitconfig                     |   1 +
>  security/landlock/audit.c                          |   4 +
>  security/landlock/audit.h                          |   1 +
>  security/landlock/cred.c                           |  14 -
>  security/landlock/domain.c                         |  67 ++++
>  security/landlock/domain.h                         |   4 +
>  security/landlock/fs.c                             | 405 ++++++++++++++++++++-
>  security/landlock/limits.h                         |   2 +-
>  security/landlock/task.c                           |  67 ----
>  .../selftests/landlock/scoped_memfd_exec_test.c    | 325 +++++++++++++++++
>  11 files changed, 812 insertions(+), 83 deletions(-)
> ---
> base-commit: 5b74b2eff1eeefe43584e5b7b348c8cd3b723d38
> change-id: 20250716-memfd-exec-ac0d582018c3
> 
> Best regards,
> -- 
> Abhinav Saxena <xandfury@gmail.com>
> 
> 

^ permalink raw reply

* Re: [PATCH RFC 2/4] landlock: implement memfd detection
From: Mickaël Salaün @ 2025-09-18 14:07 UTC (permalink / raw)
  To: Abhinav Saxena
  Cc: Fan Wu, Günther Noack, Paul Moore, James Morris,
	Serge E. Hallyn, Shuah Khan, Nathan Chancellor, Nick Desaulniers,
	Bill Wendling, Justin Stitt, linux-security-module, linux-kernel,
	linux-kselftest, llvm, Daniel Verkamp, Jeff Xu
In-Reply-To: <87v7nj7p1d.fsf@gmail.com>

On Tue, Jul 22, 2025 at 03:56:38PM -0600, Abhinav Saxena wrote:
> Fan Wu <wufan@kernel.org> writes:
> 
> > On Sat, Jul 19, 2025 at 4:13 AM Abhinav Saxena <xandfury@gmail.com> wrote:
> >>
> >> Add is_memfd_file() function to reliably detect memfd files by checking
> >> for “memfd:” prefix in dentry names on shmem-backed files. This
> >> distinguishes true memfd files from regular shmem files.
> >>
> >> Move domain_is_scoped() to domain.c for reuse across subsystems.
> >> Add comprehensive kunit tests for memfd detection edge cases.
> >>
> >> Signed-off-by: Abhinav Saxena <xandfury@gmail.com>
> >> —
> >>  security/landlock/domain.c |  67 +++++++++++++++
> >>  security/landlock/domain.h |   4 +
> >>  security/landlock/fs.c     | 210 +++++++++++++++++++++++++++++++++++++++++++++
> >>  security/landlock/task.c   |  67 —————
> >>  4 files changed, 281 insertions(+), 67 deletions(-)
> >
> > …
> >
> >>
> >> +/**
> >> + * is_memfd_file - Check if file was created via memfd_create()
> >> + * @file: File to check
> >> + *
> >> + * Returns true if @file was created via memfd_create(), false otherwise.
> >> + *
> >> + * memfd files are shmem-backed files with “memfd:” prefix in their dentry name.
> >> + * This is the definitive way to distinguish memfd files from regular shmem
> >> + * files.
> >> + */
> >> +static bool is_memfd_file(struct file *file)
> >> +{
> >> +       const struct dentry *dentry;
> >> +       const unsigned char *name;
> >> +       size_t name_len;
> >> +
> >> +       /* Fast path: basic validation */
> >> +       if (unlikely(!file))
> >> +               return false;
> >> +
> >> +       /* Must be shmem-backed first - this is the cheapest definitive check */
> >> +       if (!shmem_file(file))
> >> +               return false;

In which case a memfd would not be detected by this function?
shmem_file_operations or hugetlbfs_file_operations are always set for
memfd right?

We could export memfd_get_seals() to stay consistent over time.

> >> +
> >> +#ifdef CONFIG_MEMFD_CREATE
> >> +
> >> +       /* Validate dentry and get name info */
> >> +       dentry = file->f_path.dentry;
> >> +       if (unlikely(!dentry))
> >> +               return false;
> >> +
> >> +       name_len = dentry->d_name.len;
> >> +       name = dentry->d_name.name;
> >> +
> >> +       /* memfd files always have “memfd:” prefix (6 characters) */
> >> +       if (name_len < 6 || unlikely(!name))
> >> +               return false;
> >> +
> >> +       /* Check for exact “memfd:” prefix */
> >> +       return memcmp(name, “memfd:”, 6) == 0;
> >> +#else
> >> +       return false;
> >> +#endif
> >
> > I was trying to do something similar early this year but didn’t hear
> > feedback from the linux-mm folks.
> > <https://lore.kernel.org/linux-security-module/20250129203932.22165-1-wufan@kernel.org/>
> >
> > I have considered this approach but didn’t use it. My concern is,
> > potentially a malicious user can create a file in a shmem fs, e.g.
> > tmpfs , with the “memfd:” prefix, which can be used to bypass security
> > policy.

In the case of Landlock that should not be a security issue but a
compatibility issue, which is not better.

> > (Resending this message due to a misconfiguration with my email
> > client. Apologies for any inconvenience.)
> >
> > -Fan
> 
> Hi Fan,
> 
> Thanks for your comments.
> 
> I agree that an LSM hook into memfd_create() would be a much better
> solution. In the absence of such a function, do you think adding a
> `d_unlinked(dentry)` check could serve as an additional verification?
> 
> I say things since I *think* that legitimate memfd files are always
> unlinked while spoofed tmpfs files remain linked. I could be wrong
> though.

We should just use the same checks used by the memfd-related
syscalls/operations to detect such file.

> 
> In any case, we can test this approach using kprobes to validate
> the behavior.
> 
> -Abhinav


^ permalink raw reply

* [RFC PATCH v2] lsm,selinux: introduce LSM_ATTR_UNSHARE and wire it up for SELinux
From: Stephen Smalley @ 2025-09-18 13:59 UTC (permalink / raw)
  To: linux-security-module, selinux
  Cc: paul, omosnace, john.johansen, serge, casey, Stephen Smalley

RFC-only, will ultimately split the LSM-only changes to their own
patch for submission. I have now tested this with the corresponding
selinux userspace change that you can find at
https://lore.kernel.org/selinux/20250918135118.9896-2-stephen.smalley.work@gmail.com/
and also verified that my modified systemd-nspawn still works when
starting containers with their own SELinux namespace.

This defines a new LSM_ATTR_UNSHARE attribute for the
lsm_set_self_attr(2) system call and wires it up for SELinux to invoke
the underlying function for unsharing the SELinux namespace. As with
the selinuxfs interface, this immediately unshares the SELinux
namespace of the current process just like an unshare(2) system call
would do for other namespaces. I have not yet explored the
alternatives of deferring the unshare to the next unshare(2),
clone(2), or execve(2) call and would want to first confirm that doing
so does not introduce any issues in the kernel or make it harder to
integrate with existing container runtimes.

Differences between this syscall interface and the selinuxfs interface
that need discussion before moving forward:

1. The syscall interface does not currently check any Linux capability
or DAC permissions, whereas the selinuxfs interface can only be set by
uid-0 or CAP_DAC_OVERRIDE processes. We need to decide what if any
capability or DAC check should apply to this syscall interface and if
any, add the checks to either the LSM framework code or to the SELinux
hook function.

Pros: Checking a capability or DAC permissions prevents misuse of this
interface by unprivileged processes, particularly on systems with
policies that do not yet define any of the new SELinux permissions
introduced for controlling this operation. This is a potential concern
on Linux distributions that do not tightly coordinate kernel updates
with policy updates (or where users may choose to deploy upstream
kernels on their own), but not on Android.

Cons: Checking a capability or DAC permissions requires any process
that uses this facility to have the corresponding capability or
permissions, which might otherwise be unnecessary and create
additional risks. This is less likely if we use a capability already
required by container runtimes and similar components that might
leverage this facility for unsharing SELinux namespaces.

2. The syscall interface checks a new SELinux unshare_selinuxns
permission in the process2 class between the task SID and itself,
similar to other checks for setting process attributes. This means
that:
    allow domain self:process2 *; -or-
    allow domain self:process2 ~anything-other-than-unshare_selinuxns; -or-
    allow domain self:process2 unshare_selinuxns;
would allow a process to unshare its SELinux namespace.

The selinuxfs interface checks a new unshare permission in the
security class between the task SID and the security initial SID,
likewise similar to other checks for setting selinuxfs attributes.
This means that:
    allow domain security_t:security *; -or-
    allow domain security_t:security ~anything-other-than-unshare; -or-
    allow domain security_t:security unshare;
would allow a process to unshare its SELinux namespace.

Technically, the selinuxfs interface also currently requires open and
write access to the selinuxfs node; hence:
    allow domain security_t:file { open write };
is also required for the selinuxfs interface.

We need to decide what we want the SELinux check(s) to be for the
syscall and whether it should be more like the former (process
attributes) or more like the latter (security policy settings). Note
that the permission name itself is unimportant here and only differs
because it seemed less evident in the process2 class that we are
talking about a SELinux namespace otherwise.

Regardless, either form of allow rule can be prohibited in policies
via neverallow rules on systems that enforce their usage
(e.g. Android, not necessarily on Linux distributions).

3. The selinuxfs interface currently offers more functionality than I
have implemented here for the sycall interface, including:

a) the ability to read the selinuxfs node to see if your namespace has
been unshared, which should be easily implementable via
lsm_get_self_attr(2).  However, questions remain as to when that
should return 1 versus 0 (currently returns 1 whenever your namespace
is NOT the initial SELinux namespace, useful for the testsuite to
detect it is in a child, but could instead be reset to 0 by a
subsequent policy load to indicate completion of the setup of the
namespace, thus hiding from child processes that they are in a child
namespace once its policy has been loaded).

b) the abilities to get and set the maximum number of SELinux
namespaces (via a /sys/fs/selinux/maxns node) and to get and set the
maximum depth for SELinux namespaces (via a /sys/fs/selinux/maxnsdepth
node). These could be left in selinuxfs or migrated to some other LSM
management APIs since they are global in scope, not per-process
attributes.

Signed-off-by: Stephen Smalley <stephen.smalley.work@gmail.com>
---
v2 fixes a typo (PROCESS->PROCESS2) and is now tested.

 include/uapi/linux/lsm.h            | 1 +
 security/selinux/hooks.c            | 8 ++++++++
 security/selinux/include/classmap.h | 4 +++-
 3 files changed, 12 insertions(+), 1 deletion(-)

diff --git a/include/uapi/linux/lsm.h b/include/uapi/linux/lsm.h
index 938593dfd5da..fb1b4a8aa639 100644
--- a/include/uapi/linux/lsm.h
+++ b/include/uapi/linux/lsm.h
@@ -83,6 +83,7 @@ struct lsm_ctx {
 #define LSM_ATTR_KEYCREATE	103
 #define LSM_ATTR_PREV		104
 #define LSM_ATTR_SOCKCREATE	105
+#define LSM_ATTR_UNSHARE	106
 
 /*
  * LSM_FLAG_XXX definitions identify special handling instructions
diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c
index f48483383d6e..1e34a16b7954 100644
--- a/security/selinux/hooks.c
+++ b/security/selinux/hooks.c
@@ -6816,6 +6816,10 @@ static int selinux_lsm_setattr(u64 attr, void *value, size_t size)
 		error = avc_has_perm(state, mysid, mysid, SECCLASS_PROCESS,
 				     PROCESS__SETCURRENT, NULL);
 		break;
+	case LSM_ATTR_UNSHARE:
+		error = avc_has_perm(state, mysid, mysid, SECCLASS_PROCESS2,
+				     PROCESS2__UNSHARE_SELINUXNS, NULL);
+		break;
 	default:
 		error = -EOPNOTSUPP;
 		break;
@@ -6927,6 +6931,10 @@ static int selinux_lsm_setattr(u64 attr, void *value, size_t size)
 		}
 
 		tsec->sid = sid;
+	} else if (attr == LSM_ATTR_UNSHARE) {
+		error = selinux_state_create(new);
+		if (error)
+			goto abort_change;
 	} else {
 		error = -EINVAL;
 		goto abort_change;
diff --git a/security/selinux/include/classmap.h b/security/selinux/include/classmap.h
index be52ebb6b94a..07fe316308cd 100644
--- a/security/selinux/include/classmap.h
+++ b/security/selinux/include/classmap.h
@@ -60,7 +60,9 @@ const struct security_class_mapping secclass_map[] = {
 	    "siginh",	    "setrlimit",     "rlimitinh",   "dyntransition",
 	    "setcurrent",   "execmem",	     "execstack",   "execheap",
 	    "setkeycreate", "setsockcreate", "getrlimit",   NULL } },
-	{ "process2", { "nnp_transition", "nosuid_transition", NULL } },
+	{ "process2",
+	  { "nnp_transition", "nosuid_transition", "unshare_selinuxns",
+	    NULL } },
 	{ "system",
 	  { "ipc_info", "syslog_read", "syslog_mod", "syslog_console",
 	    "module_request", "module_load", "firmware_load",
-- 
2.50.1


^ permalink raw reply related

* [PATCH v3 35/35] sched: Enable capability analysis for core.c and fair.c
From: Marco Elver @ 2025-09-18 13:59 UTC (permalink / raw)
  To: elver, Peter Zijlstra, Boqun Feng, Ingo Molnar, Will Deacon
  Cc: David S. Miller, Luc Van Oostenryck, Paul E. McKenney,
	Alexander Potapenko, Arnd Bergmann, Bart Van Assche,
	Bill Wendling, Christoph Hellwig, Dmitry Vyukov, Eric Dumazet,
	Frederic Weisbecker, Greg Kroah-Hartman, Herbert Xu, Ian Rogers,
	Jann Horn, Joel Fernandes, Jonathan Corbet, Josh Triplett,
	Justin Stitt, Kees Cook, Kentaro Takeda, Lukas Bulwahn,
	Mark Rutland, Mathieu Desnoyers, Miguel Ojeda, Nathan Chancellor,
	Neeraj Upadhyay, Nick Desaulniers, Steven Rostedt, Tetsuo Handa,
	Thomas Gleixner, Thomas Graf, Uladzislau Rezki, Waiman Long,
	kasan-dev, linux-crypto, linux-doc, linux-kbuild, linux-kernel,
	linux-mm, linux-security-module, linux-sparse, llvm, rcu,
	Ingo Molnar
In-Reply-To: <20250918140451.1289454-1-elver@google.com>

This demonstrates a larger conversion to use Clang's capability
analysis. The benefit is additional static checking of locking rules,
along with better documentation.

Notably, kernel/sched contains sufficiently complex synchronization
patterns, and application to core.c & fair.c demonstrates that the
latest Clang version has become powerful enough to start applying this
to more complex subsystems (with some modest annotations and changes).

Signed-off-by: Marco Elver <elver@google.com>
Cc: Peter Zijlstra <peterz@infradead.org>
Cc: Ingo Molnar <mingo@redhat.com>
---
v3:
* New patch.
---
 include/linux/sched.h                       |   6 +-
 include/linux/sched/signal.h                |   4 +-
 include/linux/sched/task.h                  |   5 +-
 include/linux/sched/wake_q.h                |   3 +
 kernel/sched/Makefile                       |   3 +
 kernel/sched/core.c                         |  89 +++++++++++-----
 kernel/sched/fair.c                         |   9 +-
 kernel/sched/sched.h                        | 110 +++++++++++++-------
 scripts/capability-analysis-suppression.txt |   1 +
 9 files changed, 162 insertions(+), 68 deletions(-)

diff --git a/include/linux/sched.h b/include/linux/sched.h
index f8188b833350..bc2be55a9f47 100644
--- a/include/linux/sched.h
+++ b/include/linux/sched.h
@@ -2102,9 +2102,9 @@ static inline int _cond_resched(void)
 	_cond_resched();			\
 })
 
-extern int __cond_resched_lock(spinlock_t *lock);
-extern int __cond_resched_rwlock_read(rwlock_t *lock);
-extern int __cond_resched_rwlock_write(rwlock_t *lock);
+extern int __cond_resched_lock(spinlock_t *lock) __must_hold(lock);
+extern int __cond_resched_rwlock_read(rwlock_t *lock) __must_hold_shared(lock);
+extern int __cond_resched_rwlock_write(rwlock_t *lock) __must_hold(lock);
 
 #define MIGHT_RESCHED_RCU_SHIFT		8
 #define MIGHT_RESCHED_PREEMPT_MASK	((1U << MIGHT_RESCHED_RCU_SHIFT) - 1)
diff --git a/include/linux/sched/signal.h b/include/linux/sched/signal.h
index bc7f83b012fb..6f581a750e84 100644
--- a/include/linux/sched/signal.h
+++ b/include/linux/sched/signal.h
@@ -734,10 +734,12 @@ static inline int thread_group_empty(struct task_struct *p)
 		(thread_group_leader(p) && !thread_group_empty(p))
 
 extern struct sighand_struct *lock_task_sighand(struct task_struct *task,
-						unsigned long *flags);
+						unsigned long *flags)
+	__acquires(&task->sighand->siglock);
 
 static inline void unlock_task_sighand(struct task_struct *task,
 						unsigned long *flags)
+	__releases(&task->sighand->siglock)
 {
 	spin_unlock_irqrestore(&task->sighand->siglock, *flags);
 }
diff --git a/include/linux/sched/task.h b/include/linux/sched/task.h
index ea41795a352b..4db5c9323989 100644
--- a/include/linux/sched/task.h
+++ b/include/linux/sched/task.h
@@ -215,15 +215,18 @@ static inline struct vm_struct *task_stack_vm_area(const struct task_struct *t)
  * neither inside nor outside.
  */
 static inline void task_lock(struct task_struct *p)
+	__acquires(&p->alloc_lock)
 {
 	spin_lock(&p->alloc_lock);
 }
 
 static inline void task_unlock(struct task_struct *p)
+	__releases(&p->alloc_lock)
 {
 	spin_unlock(&p->alloc_lock);
 }
 
-DEFINE_GUARD(task_lock, struct task_struct *, task_lock(_T), task_unlock(_T))
+DEFINE_LOCK_GUARD_1(task_lock, struct task_struct, task_lock(_T->lock), task_unlock(_T->lock))
+DECLARE_LOCK_GUARD_1_ATTRS(task_lock, __assumes_cap(_T->alloc_lock), /* */)
 
 #endif /* _LINUX_SCHED_TASK_H */
diff --git a/include/linux/sched/wake_q.h b/include/linux/sched/wake_q.h
index 0f28b4623ad4..765bbc3d54be 100644
--- a/include/linux/sched/wake_q.h
+++ b/include/linux/sched/wake_q.h
@@ -66,6 +66,7 @@ extern void wake_up_q(struct wake_q_head *head);
 /* Spin unlock helpers to unlock and call wake_up_q with preempt disabled */
 static inline
 void raw_spin_unlock_wake(raw_spinlock_t *lock, struct wake_q_head *wake_q)
+	__releases(lock)
 {
 	guard(preempt)();
 	raw_spin_unlock(lock);
@@ -77,6 +78,7 @@ void raw_spin_unlock_wake(raw_spinlock_t *lock, struct wake_q_head *wake_q)
 
 static inline
 void raw_spin_unlock_irq_wake(raw_spinlock_t *lock, struct wake_q_head *wake_q)
+	__releases(lock)
 {
 	guard(preempt)();
 	raw_spin_unlock_irq(lock);
@@ -89,6 +91,7 @@ void raw_spin_unlock_irq_wake(raw_spinlock_t *lock, struct wake_q_head *wake_q)
 static inline
 void raw_spin_unlock_irqrestore_wake(raw_spinlock_t *lock, unsigned long flags,
 				     struct wake_q_head *wake_q)
+	__releases(lock)
 {
 	guard(preempt)();
 	raw_spin_unlock_irqrestore(lock, flags);
diff --git a/kernel/sched/Makefile b/kernel/sched/Makefile
index 8ae86371ddcd..8603987ce4c1 100644
--- a/kernel/sched/Makefile
+++ b/kernel/sched/Makefile
@@ -1,5 +1,8 @@
 # SPDX-License-Identifier: GPL-2.0
 
+CAPABILITY_ANALYSIS_core.o := y
+CAPABILITY_ANALYSIS_fair.o := y
+
 # The compilers are complaining about unused variables inside an if(0) scope
 # block. This is daft, shut them up.
 ccflags-y += $(call cc-disable-warning, unused-but-set-variable)
diff --git a/kernel/sched/core.c b/kernel/sched/core.c
index be00629f0ba4..233f774754b6 100644
--- a/kernel/sched/core.c
+++ b/kernel/sched/core.c
@@ -393,6 +393,8 @@ static atomic_t sched_core_count;
 static struct cpumask sched_core_mask;
 
 static void sched_core_lock(int cpu, unsigned long *flags)
+	__capability_unsafe(/* acquires multiple */)
+	__acquires(&runqueues.__lock) /* overapproximation */
 {
 	const struct cpumask *smt_mask = cpu_smt_mask(cpu);
 	int t, i = 0;
@@ -403,6 +405,8 @@ static void sched_core_lock(int cpu, unsigned long *flags)
 }
 
 static void sched_core_unlock(int cpu, unsigned long *flags)
+	__capability_unsafe(/* releases multiple */)
+	__releases(&runqueues.__lock) /* overapproximation */
 {
 	const struct cpumask *smt_mask = cpu_smt_mask(cpu);
 	int t;
@@ -627,6 +631,7 @@ EXPORT_SYMBOL(__trace_set_current_state);
  */
 
 void raw_spin_rq_lock_nested(struct rq *rq, int subclass)
+	__capability_unsafe()
 {
 	raw_spinlock_t *lock;
 
@@ -652,6 +657,7 @@ void raw_spin_rq_lock_nested(struct rq *rq, int subclass)
 }
 
 bool raw_spin_rq_trylock(struct rq *rq)
+	__capability_unsafe()
 {
 	raw_spinlock_t *lock;
 	bool ret;
@@ -693,15 +699,16 @@ void double_rq_lock(struct rq *rq1, struct rq *rq2)
 	raw_spin_rq_lock(rq1);
 	if (__rq_lockp(rq1) != __rq_lockp(rq2))
 		raw_spin_rq_lock_nested(rq2, SINGLE_DEPTH_NESTING);
+	else
+		__acquire_cap(__rq_lockp(rq2)); /* fake acquire */
 
 	double_rq_clock_clear_update(rq1, rq2);
 }
 
 /*
- * __task_rq_lock - lock the rq @p resides on.
+ * ___task_rq_lock - lock the rq @p resides on.
  */
-struct rq *__task_rq_lock(struct task_struct *p, struct rq_flags *rf)
-	__acquires(rq->lock)
+struct rq *___task_rq_lock(struct task_struct *p, struct rq_flags *rf)
 {
 	struct rq *rq;
 
@@ -724,9 +731,7 @@ struct rq *__task_rq_lock(struct task_struct *p, struct rq_flags *rf)
 /*
  * task_rq_lock - lock p->pi_lock and lock the rq @p resides on.
  */
-struct rq *task_rq_lock(struct task_struct *p, struct rq_flags *rf)
-	__acquires(p->pi_lock)
-	__acquires(rq->lock)
+struct rq *_task_rq_lock(struct task_struct *p, struct rq_flags *rf)
 {
 	struct rq *rq;
 
@@ -2498,6 +2503,7 @@ static inline bool is_cpu_allowed(struct task_struct *p, int cpu)
  */
 static struct rq *move_queued_task(struct rq *rq, struct rq_flags *rf,
 				   struct task_struct *p, int new_cpu)
+	__must_hold(__rq_lockp(rq))
 {
 	lockdep_assert_rq_held(rq);
 
@@ -2544,6 +2550,7 @@ struct set_affinity_pending {
  */
 static struct rq *__migrate_task(struct rq *rq, struct rq_flags *rf,
 				 struct task_struct *p, int dest_cpu)
+	__must_hold(__rq_lockp(rq))
 {
 	/* Affinity changed (again). */
 	if (!is_cpu_allowed(p, dest_cpu))
@@ -2580,6 +2587,12 @@ static int migration_cpu_stop(void *data)
 	 */
 	flush_smp_call_function_queue();
 
+	/*
+	 * We may change the underlying rq, but the locks held will
+	 * appropriately be "transferred" when switching.
+	 */
+	capability_unsafe_alias(rq);
+
 	raw_spin_lock(&p->pi_lock);
 	rq_lock(rq, &rf);
 
@@ -2689,6 +2702,8 @@ int push_cpu_stop(void *arg)
 	if (!lowest_rq)
 		goto out_unlock;
 
+	lockdep_assert_rq_held(lowest_rq);
+
 	// XXX validate p is still the highest prio task
 	if (task_rq(p) == rq) {
 		move_queued_task_locked(rq, lowest_rq, p);
@@ -2934,8 +2949,7 @@ void release_user_cpus_ptr(struct task_struct *p)
  */
 static int affine_move_task(struct rq *rq, struct task_struct *p, struct rq_flags *rf,
 			    int dest_cpu, unsigned int flags)
-	__releases(rq->lock)
-	__releases(p->pi_lock)
+	__releases(__rq_lockp(rq), &p->pi_lock)
 {
 	struct set_affinity_pending my_pending = { }, *pending = NULL;
 	bool stop_pending, complete = false;
@@ -3090,8 +3104,7 @@ static int __set_cpus_allowed_ptr_locked(struct task_struct *p,
 					 struct affinity_context *ctx,
 					 struct rq *rq,
 					 struct rq_flags *rf)
-	__releases(rq->lock)
-	__releases(p->pi_lock)
+	__releases(__rq_lockp(rq), &p->pi_lock)
 {
 	const struct cpumask *cpu_allowed_mask = task_cpu_possible_mask(p);
 	const struct cpumask *cpu_valid_mask = cpu_active_mask;
@@ -4383,29 +4396,30 @@ static bool __task_needs_rq_lock(struct task_struct *p)
  */
 int task_call_func(struct task_struct *p, task_call_f func, void *arg)
 {
-	struct rq *rq = NULL;
 	struct rq_flags rf;
 	int ret;
 
 	raw_spin_lock_irqsave(&p->pi_lock, rf.flags);
 
-	if (__task_needs_rq_lock(p))
-		rq = __task_rq_lock(p, &rf);
+	if (__task_needs_rq_lock(p)) {
+		struct rq *rq = __task_rq_lock(p, &rf);
 
-	/*
-	 * At this point the task is pinned; either:
-	 *  - blocked and we're holding off wakeups	 (pi->lock)
-	 *  - woken, and we're holding off enqueue	 (rq->lock)
-	 *  - queued, and we're holding off schedule	 (rq->lock)
-	 *  - running, and we're holding off de-schedule (rq->lock)
-	 *
-	 * The called function (@func) can use: task_curr(), p->on_rq and
-	 * p->__state to differentiate between these states.
-	 */
-	ret = func(p, arg);
+		/*
+		 * At this point the task is pinned; either:
+		 *  - blocked and we're holding off wakeups	 (pi->lock)
+		 *  - woken, and we're holding off enqueue	 (rq->lock)
+		 *  - queued, and we're holding off schedule	 (rq->lock)
+		 *  - running, and we're holding off de-schedule (rq->lock)
+		 *
+		 * The called function (@func) can use: task_curr(), p->on_rq and
+		 * p->__state to differentiate between these states.
+		 */
+		ret = func(p, arg);
 
-	if (rq)
 		rq_unlock(rq, &rf);
+	} else {
+		ret = func(p, arg);
+	}
 
 	raw_spin_unlock_irqrestore(&p->pi_lock, rf.flags);
 	return ret;
@@ -5078,6 +5092,8 @@ void balance_callbacks(struct rq *rq, struct balance_callback *head)
 
 static inline void
 prepare_lock_switch(struct rq *rq, struct task_struct *next, struct rq_flags *rf)
+	__releases(__rq_lockp(rq))
+	__acquires(__rq_lockp(this_rq()))
 {
 	/*
 	 * Since the runqueue lock will be released by the next
@@ -5091,9 +5107,15 @@ prepare_lock_switch(struct rq *rq, struct task_struct *next, struct rq_flags *rf
 	/* this is a valid case when another task releases the spinlock */
 	rq_lockp(rq)->owner = next;
 #endif
+	/*
+	 * Model the rq reference switcheroo.
+	 */
+	__release(__rq_lockp(rq));
+	__acquire(__rq_lockp(this_rq()));
 }
 
 static inline void finish_lock_switch(struct rq *rq)
+	__releases(__rq_lockp(rq))
 {
 	/*
 	 * If we are tracking spinlock dependencies then we have to
@@ -5149,6 +5171,7 @@ static inline void kmap_local_sched_in(void)
 static inline void
 prepare_task_switch(struct rq *rq, struct task_struct *prev,
 		    struct task_struct *next)
+	__must_hold(__rq_lockp(rq))
 {
 	kcov_prepare_switch(prev);
 	sched_info_switch(rq, prev, next);
@@ -5180,7 +5203,7 @@ prepare_task_switch(struct rq *rq, struct task_struct *prev,
  * because prev may have moved to another CPU.
  */
 static struct rq *finish_task_switch(struct task_struct *prev)
-	__releases(rq->lock)
+	__releases(__rq_lockp(this_rq()))
 {
 	struct rq *rq = this_rq();
 	struct mm_struct *mm = rq->prev_mm;
@@ -5268,7 +5291,7 @@ static struct rq *finish_task_switch(struct task_struct *prev)
  * @prev: the thread we just switched away from.
  */
 asmlinkage __visible void schedule_tail(struct task_struct *prev)
-	__releases(rq->lock)
+	__releases(__rq_lockp(this_rq()))
 {
 	/*
 	 * New tasks start with FORK_PREEMPT_COUNT, see there and
@@ -5300,6 +5323,7 @@ asmlinkage __visible void schedule_tail(struct task_struct *prev)
 static __always_inline struct rq *
 context_switch(struct rq *rq, struct task_struct *prev,
 	       struct task_struct *next, struct rq_flags *rf)
+	__releases(__rq_lockp(rq))
 {
 	prepare_task_switch(rq, prev, next);
 
@@ -5980,6 +6004,7 @@ static void prev_balance(struct rq *rq, struct task_struct *prev,
  */
 static inline struct task_struct *
 __pick_next_task(struct rq *rq, struct task_struct *prev, struct rq_flags *rf)
+	__must_hold(__rq_lockp(rq))
 {
 	const struct sched_class *class;
 	struct task_struct *p;
@@ -6072,6 +6097,7 @@ static void queue_core_balance(struct rq *rq);
 
 static struct task_struct *
 pick_next_task(struct rq *rq, struct task_struct *prev, struct rq_flags *rf)
+	__must_hold(__rq_lockp(rq))
 {
 	struct task_struct *next, *p, *max = NULL;
 	const struct cpumask *smt_mask;
@@ -6371,6 +6397,7 @@ static bool steal_cookie_task(int cpu, struct sched_domain *sd)
 }
 
 static void sched_core_balance(struct rq *rq)
+	__must_hold(__rq_lockp(rq))
 {
 	struct sched_domain *sd;
 	int cpu = cpu_of(rq);
@@ -6516,6 +6543,7 @@ static inline void sched_core_cpu_dying(unsigned int cpu) {}
 
 static struct task_struct *
 pick_next_task(struct rq *rq, struct task_struct *prev, struct rq_flags *rf)
+	__must_hold(__rq_lockp(rq))
 {
 	return __pick_next_task(rq, prev, rf);
 }
@@ -8173,6 +8201,12 @@ static int __balance_push_cpu_stop(void *arg)
 	struct rq_flags rf;
 	int cpu;
 
+	/*
+	 * We may change the underlying rq, but the locks held will
+	 * appropriately be "transferred" when switching.
+	 */
+	capability_unsafe_alias(rq);
+
 	raw_spin_lock_irq(&p->pi_lock);
 	rq_lock(rq, &rf);
 
@@ -8200,6 +8234,7 @@ static DEFINE_PER_CPU(struct cpu_stop_work, push_work);
  * effective when the hotplug motion is down.
  */
 static void balance_push(struct rq *rq)
+	__must_hold(__rq_lockp(rq))
 {
 	struct task_struct *push_task = rq->curr;
 
diff --git a/kernel/sched/fair.c b/kernel/sched/fair.c
index b173a059315c..4c4663d99996 100644
--- a/kernel/sched/fair.c
+++ b/kernel/sched/fair.c
@@ -2876,6 +2876,7 @@ static int preferred_group_nid(struct task_struct *p, int nid)
 }
 
 static void task_numa_placement(struct task_struct *p)
+	__capability_unsafe(/* conditional locking */)
 {
 	int seq, nid, max_nid = NUMA_NO_NODE;
 	unsigned long max_faults = 0;
@@ -4806,7 +4807,8 @@ static inline unsigned long cfs_rq_load_avg(struct cfs_rq *cfs_rq)
 	return cfs_rq->avg.load_avg;
 }
 
-static int sched_balance_newidle(struct rq *this_rq, struct rq_flags *rf);
+static int sched_balance_newidle(struct rq *this_rq, struct rq_flags *rf)
+	__must_hold(__rq_lockp(this_rq));
 
 static inline unsigned long task_util(struct task_struct *p)
 {
@@ -6155,6 +6157,7 @@ static bool distribute_cfs_runtime(struct cfs_bandwidth *cfs_b)
  * used to track this state.
  */
 static int do_sched_cfs_period_timer(struct cfs_bandwidth *cfs_b, int overrun, unsigned long flags)
+	__must_hold(&cfs_b->lock)
 {
 	int throttled;
 
@@ -8622,6 +8625,7 @@ static void set_cpus_allowed_fair(struct task_struct *p, struct affinity_context
 
 static int
 balance_fair(struct rq *rq, struct task_struct *prev, struct rq_flags *rf)
+	__must_hold(__rq_lockp(rq))
 {
 	if (sched_fair_runnable(rq))
 		return 1;
@@ -8769,6 +8773,7 @@ static void set_next_task_fair(struct rq *rq, struct task_struct *p, bool first)
 
 struct task_struct *
 pick_next_task_fair(struct rq *rq, struct task_struct *prev, struct rq_flags *rf)
+	__must_hold(__rq_lockp(rq))
 {
 	struct sched_entity *se;
 	struct task_struct *p;
@@ -8855,6 +8860,7 @@ pick_next_task_fair(struct rq *rq, struct task_struct *prev, struct rq_flags *rf
 }
 
 static struct task_struct *__pick_next_task_fair(struct rq *rq, struct task_struct *prev)
+	__must_hold(__rq_lockp(rq))
 {
 	return pick_next_task_fair(rq, prev, NULL);
 }
@@ -12691,6 +12697,7 @@ static inline void nohz_newidle_balance(struct rq *this_rq) { }
  *   > 0 - success, new (fair) tasks present
  */
 static int sched_balance_newidle(struct rq *this_rq, struct rq_flags *rf)
+	__must_hold(__rq_lockp(this_rq))
 {
 	unsigned long next_balance = jiffies + HZ;
 	int this_cpu = this_rq->cpu;
diff --git a/kernel/sched/sched.h b/kernel/sched/sched.h
index be9745d104f7..8fdbf2e3fb1e 100644
--- a/kernel/sched/sched.h
+++ b/kernel/sched/sched.h
@@ -1322,8 +1322,13 @@ static inline bool is_migration_disabled(struct task_struct *p)
 
 DECLARE_PER_CPU_SHARED_ALIGNED(struct rq, runqueues);
 
+static __always_inline struct rq *__this_rq(void)
+{
+	return this_cpu_ptr(&runqueues);
+}
+
 #define cpu_rq(cpu)		(&per_cpu(runqueues, (cpu)))
-#define this_rq()		this_cpu_ptr(&runqueues)
+#define this_rq()		__this_rq()
 #define task_rq(p)		cpu_rq(task_cpu(p))
 #define cpu_curr(cpu)		(cpu_rq(cpu)->curr)
 #define raw_rq()		raw_cpu_ptr(&runqueues)
@@ -1368,6 +1373,7 @@ static inline raw_spinlock_t *rq_lockp(struct rq *rq)
 }
 
 static inline raw_spinlock_t *__rq_lockp(struct rq *rq)
+	__returns_cap(rq_lockp(rq)) /* alias them */
 {
 	if (rq->core_enabled)
 		return &rq->core->__lock;
@@ -1464,6 +1470,7 @@ static inline raw_spinlock_t *rq_lockp(struct rq *rq)
 }
 
 static inline raw_spinlock_t *__rq_lockp(struct rq *rq)
+	__returns_cap(rq_lockp(rq)) /* alias them */
 {
 	return &rq->__lock;
 }
@@ -1506,32 +1513,42 @@ static inline bool rt_group_sched_enabled(void)
 #endif /* !CONFIG_RT_GROUP_SCHED */
 
 static inline void lockdep_assert_rq_held(struct rq *rq)
+	__assumes_cap(__rq_lockp(rq))
 {
 	lockdep_assert_held(__rq_lockp(rq));
 }
 
-extern void raw_spin_rq_lock_nested(struct rq *rq, int subclass);
-extern bool raw_spin_rq_trylock(struct rq *rq);
-extern void raw_spin_rq_unlock(struct rq *rq);
+extern void raw_spin_rq_lock_nested(struct rq *rq, int subclass)
+	__acquires(__rq_lockp(rq));
+
+extern bool raw_spin_rq_trylock(struct rq *rq)
+	__cond_acquires(true, __rq_lockp(rq));
+
+extern void raw_spin_rq_unlock(struct rq *rq)
+	__releases(__rq_lockp(rq));
 
 static inline void raw_spin_rq_lock(struct rq *rq)
+	__acquires(__rq_lockp(rq))
 {
 	raw_spin_rq_lock_nested(rq, 0);
 }
 
 static inline void raw_spin_rq_lock_irq(struct rq *rq)
+	__acquires(__rq_lockp(rq))
 {
 	local_irq_disable();
 	raw_spin_rq_lock(rq);
 }
 
 static inline void raw_spin_rq_unlock_irq(struct rq *rq)
+	__releases(__rq_lockp(rq))
 {
 	raw_spin_rq_unlock(rq);
 	local_irq_enable();
 }
 
 static inline unsigned long _raw_spin_rq_lock_irqsave(struct rq *rq)
+	__acquires(__rq_lockp(rq))
 {
 	unsigned long flags;
 
@@ -1542,6 +1559,7 @@ static inline unsigned long _raw_spin_rq_lock_irqsave(struct rq *rq)
 }
 
 static inline void raw_spin_rq_unlock_irqrestore(struct rq *rq, unsigned long flags)
+	__releases(__rq_lockp(rq))
 {
 	raw_spin_rq_unlock(rq);
 	local_irq_restore(flags);
@@ -1790,17 +1808,15 @@ static inline void rq_repin_lock(struct rq *rq, struct rq_flags *rf)
 	rq->clock_update_flags |= rf->clock_update_flags;
 }
 
-extern
-struct rq *__task_rq_lock(struct task_struct *p, struct rq_flags *rf)
-	__acquires(rq->lock);
+#define __task_rq_lock(...) __acquire_ret(___task_rq_lock(__VA_ARGS__), __rq_lockp(__ret))
+extern struct rq *___task_rq_lock(struct task_struct *p, struct rq_flags *rf) __acquires_ret;
 
-extern
-struct rq *task_rq_lock(struct task_struct *p, struct rq_flags *rf)
-	__acquires(p->pi_lock)
-	__acquires(rq->lock);
+#define task_rq_lock(...) __acquire_ret(_task_rq_lock(__VA_ARGS__), __rq_lockp(__ret))
+extern struct rq *_task_rq_lock(struct task_struct *p, struct rq_flags *rf)
+	__acquires(&p->pi_lock) __acquires_ret;
 
 static inline void __task_rq_unlock(struct rq *rq, struct rq_flags *rf)
-	__releases(rq->lock)
+	__releases(__rq_lockp(rq))
 {
 	rq_unpin_lock(rq, rf);
 	raw_spin_rq_unlock(rq);
@@ -1808,8 +1824,7 @@ static inline void __task_rq_unlock(struct rq *rq, struct rq_flags *rf)
 
 static inline void
 task_rq_unlock(struct rq *rq, struct task_struct *p, struct rq_flags *rf)
-	__releases(rq->lock)
-	__releases(p->pi_lock)
+	__releases(__rq_lockp(rq), &p->pi_lock)
 {
 	rq_unpin_lock(rq, rf);
 	raw_spin_rq_unlock(rq);
@@ -1820,44 +1835,45 @@ DEFINE_LOCK_GUARD_1(task_rq_lock, struct task_struct,
 		    _T->rq = task_rq_lock(_T->lock, &_T->rf),
 		    task_rq_unlock(_T->rq, _T->lock, &_T->rf),
 		    struct rq *rq; struct rq_flags rf)
+DECLARE_LOCK_GUARD_1_ATTRS(task_rq_lock, __assumes_cap(_T->pi_lock), /* */)
 
 static inline void rq_lock_irqsave(struct rq *rq, struct rq_flags *rf)
-	__acquires(rq->lock)
+	__acquires(__rq_lockp(rq))
 {
 	raw_spin_rq_lock_irqsave(rq, rf->flags);
 	rq_pin_lock(rq, rf);
 }
 
 static inline void rq_lock_irq(struct rq *rq, struct rq_flags *rf)
-	__acquires(rq->lock)
+	__acquires(__rq_lockp(rq))
 {
 	raw_spin_rq_lock_irq(rq);
 	rq_pin_lock(rq, rf);
 }
 
 static inline void rq_lock(struct rq *rq, struct rq_flags *rf)
-	__acquires(rq->lock)
+	__acquires(__rq_lockp(rq))
 {
 	raw_spin_rq_lock(rq);
 	rq_pin_lock(rq, rf);
 }
 
 static inline void rq_unlock_irqrestore(struct rq *rq, struct rq_flags *rf)
-	__releases(rq->lock)
+	__releases(__rq_lockp(rq))
 {
 	rq_unpin_lock(rq, rf);
 	raw_spin_rq_unlock_irqrestore(rq, rf->flags);
 }
 
 static inline void rq_unlock_irq(struct rq *rq, struct rq_flags *rf)
-	__releases(rq->lock)
+	__releases(__rq_lockp(rq))
 {
 	rq_unpin_lock(rq, rf);
 	raw_spin_rq_unlock_irq(rq);
 }
 
 static inline void rq_unlock(struct rq *rq, struct rq_flags *rf)
-	__releases(rq->lock)
+	__releases(__rq_lockp(rq))
 {
 	rq_unpin_lock(rq, rf);
 	raw_spin_rq_unlock(rq);
@@ -1868,18 +1884,24 @@ DEFINE_LOCK_GUARD_1(rq_lock, struct rq,
 		    rq_unlock(_T->lock, &_T->rf),
 		    struct rq_flags rf)
 
+DECLARE_LOCK_GUARD_1_ATTRS(rq_lock, __assumes_cap(__rq_lockp(_T)), /* */);
+
 DEFINE_LOCK_GUARD_1(rq_lock_irq, struct rq,
 		    rq_lock_irq(_T->lock, &_T->rf),
 		    rq_unlock_irq(_T->lock, &_T->rf),
 		    struct rq_flags rf)
 
+DECLARE_LOCK_GUARD_1_ATTRS(rq_lock_irq, __assumes_cap(__rq_lockp(_T)), /* */);
+
 DEFINE_LOCK_GUARD_1(rq_lock_irqsave, struct rq,
 		    rq_lock_irqsave(_T->lock, &_T->rf),
 		    rq_unlock_irqrestore(_T->lock, &_T->rf),
 		    struct rq_flags rf)
 
-static inline struct rq *this_rq_lock_irq(struct rq_flags *rf)
-	__acquires(rq->lock)
+DECLARE_LOCK_GUARD_1_ATTRS(rq_lock_irqsave, __assumes_cap(__rq_lockp(_T)), /* */);
+
+#define this_rq_lock_irq(...) __acquire_ret(_this_rq_lock_irq(__VA_ARGS__), __rq_lockp(__ret))
+static inline struct rq *_this_rq_lock_irq(struct rq_flags *rf) __acquires_ret
 {
 	struct rq *rq;
 
@@ -2877,8 +2899,13 @@ static inline void double_rq_clock_clear_update(struct rq *rq1, struct rq *rq2)
 #define DEFINE_LOCK_GUARD_2(name, type, _lock, _unlock, ...)				\
 __DEFINE_UNLOCK_GUARD(name, type, _unlock, type *lock2; __VA_ARGS__)			\
 static inline class_##name##_t class_##name##_constructor(type *lock, type *lock2)	\
+	__no_capability_analysis							\
 { class_##name##_t _t = { .lock = lock, .lock2 = lock2 }, *_T = &_t;			\
   _lock; return _t; }
+#define DECLARE_LOCK_GUARD_2_ATTRS(_name, _lock, _unlock)				\
+static inline class_##_name##_t class_##_name##_constructor(lock_##_name##_t *_T1,	\
+							    lock_##_name##_t *_T2) _lock; \
+static inline void class_##_name##_destructor(class_##_name##_t *_T) _unlock
 
 static inline bool rq_order_less(struct rq *rq1, struct rq *rq2)
 {
@@ -2906,7 +2933,8 @@ static inline bool rq_order_less(struct rq *rq1, struct rq *rq2)
 	return rq1->cpu < rq2->cpu;
 }
 
-extern void double_rq_lock(struct rq *rq1, struct rq *rq2);
+extern void double_rq_lock(struct rq *rq1, struct rq *rq2)
+	__acquires(__rq_lockp(rq1), __rq_lockp(rq2));
 
 #ifdef CONFIG_PREEMPTION
 
@@ -2919,9 +2947,8 @@ extern void double_rq_lock(struct rq *rq1, struct rq *rq2);
  * also adds more overhead and therefore may reduce throughput.
  */
 static inline int _double_lock_balance(struct rq *this_rq, struct rq *busiest)
-	__releases(this_rq->lock)
-	__acquires(busiest->lock)
-	__acquires(this_rq->lock)
+	__must_hold(__rq_lockp(this_rq))
+	__acquires(__rq_lockp(busiest))
 {
 	raw_spin_rq_unlock(this_rq);
 	double_rq_lock(this_rq, busiest);
@@ -2938,12 +2965,16 @@ static inline int _double_lock_balance(struct rq *this_rq, struct rq *busiest)
  * regardless of entry order into the function.
  */
 static inline int _double_lock_balance(struct rq *this_rq, struct rq *busiest)
-	__releases(this_rq->lock)
-	__acquires(busiest->lock)
-	__acquires(this_rq->lock)
+	__must_hold(__rq_lockp(this_rq))
+	__acquires(__rq_lockp(busiest))
 {
-	if (__rq_lockp(this_rq) == __rq_lockp(busiest) ||
-	    likely(raw_spin_rq_trylock(busiest))) {
+	if (__rq_lockp(this_rq) == __rq_lockp(busiest)) {
+		__acquire(__rq_lockp(busiest)); /* already held */
+		double_rq_clock_clear_update(this_rq, busiest);
+		return 0;
+	}
+
+	if (likely(raw_spin_rq_trylock(busiest))) {
 		double_rq_clock_clear_update(this_rq, busiest);
 		return 0;
 	}
@@ -2966,6 +2997,8 @@ static inline int _double_lock_balance(struct rq *this_rq, struct rq *busiest)
  * double_lock_balance - lock the busiest runqueue, this_rq is locked already.
  */
 static inline int double_lock_balance(struct rq *this_rq, struct rq *busiest)
+	__must_hold(__rq_lockp(this_rq))
+	__acquires(__rq_lockp(busiest))
 {
 	lockdep_assert_irqs_disabled();
 
@@ -2973,14 +3006,17 @@ static inline int double_lock_balance(struct rq *this_rq, struct rq *busiest)
 }
 
 static inline void double_unlock_balance(struct rq *this_rq, struct rq *busiest)
-	__releases(busiest->lock)
+	__releases(__rq_lockp(busiest))
 {
 	if (__rq_lockp(this_rq) != __rq_lockp(busiest))
 		raw_spin_rq_unlock(busiest);
+	else
+		__release(__rq_lockp(busiest)); /* fake release */
 	lock_set_subclass(&__rq_lockp(this_rq)->dep_map, 0, _RET_IP_);
 }
 
 static inline void double_lock(spinlock_t *l1, spinlock_t *l2)
+	__acquires(l1, l2)
 {
 	if (l1 > l2)
 		swap(l1, l2);
@@ -2990,6 +3026,7 @@ static inline void double_lock(spinlock_t *l1, spinlock_t *l2)
 }
 
 static inline void double_lock_irq(spinlock_t *l1, spinlock_t *l2)
+	__acquires(l1, l2)
 {
 	if (l1 > l2)
 		swap(l1, l2);
@@ -2999,6 +3036,7 @@ static inline void double_lock_irq(spinlock_t *l1, spinlock_t *l2)
 }
 
 static inline void double_raw_lock(raw_spinlock_t *l1, raw_spinlock_t *l2)
+	__acquires(l1, l2)
 {
 	if (l1 > l2)
 		swap(l1, l2);
@@ -3008,6 +3046,7 @@ static inline void double_raw_lock(raw_spinlock_t *l1, raw_spinlock_t *l2)
 }
 
 static inline void double_raw_unlock(raw_spinlock_t *l1, raw_spinlock_t *l2)
+	__releases(l1, l2)
 {
 	raw_spin_unlock(l1);
 	raw_spin_unlock(l2);
@@ -3017,6 +3056,8 @@ DEFINE_LOCK_GUARD_2(double_raw_spinlock, raw_spinlock_t,
 		    double_raw_lock(_T->lock, _T->lock2),
 		    double_raw_unlock(_T->lock, _T->lock2))
 
+DECLARE_LOCK_GUARD_2_ATTRS(double_raw_spinlock, __assumes_cap(_T1) __assumes_cap(_T2), /* */);
+
 /*
  * double_rq_unlock - safely unlock two runqueues
  *
@@ -3024,13 +3065,12 @@ DEFINE_LOCK_GUARD_2(double_raw_spinlock, raw_spinlock_t,
  * you need to do so manually after calling.
  */
 static inline void double_rq_unlock(struct rq *rq1, struct rq *rq2)
-	__releases(rq1->lock)
-	__releases(rq2->lock)
+	__releases(__rq_lockp(rq1), __rq_lockp(rq2))
 {
 	if (__rq_lockp(rq1) != __rq_lockp(rq2))
 		raw_spin_rq_unlock(rq2);
 	else
-		__release(rq2->lock);
+		__release(__rq_lockp(rq2)); /* fake release */
 	raw_spin_rq_unlock(rq1);
 }
 
diff --git a/scripts/capability-analysis-suppression.txt b/scripts/capability-analysis-suppression.txt
index 95fb0b65a8e6..7ecd888ac522 100644
--- a/scripts/capability-analysis-suppression.txt
+++ b/scripts/capability-analysis-suppression.txt
@@ -26,6 +26,7 @@ src:*include/linux/refcount.h=emit
 src:*include/linux/rhashtable.h=emit
 src:*include/linux/rwlock*.h=emit
 src:*include/linux/rwsem.h=emit
+src:*include/linux/sched*=emit
 src:*include/linux/seqlock*.h=emit
 src:*include/linux/spinlock*.h=emit
 src:*include/linux/srcu*.h=emit
-- 
2.51.0.384.g4c02a37b29-goog


^ permalink raw reply related


This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox