Skip to content

Commit bde5ed2

Browse files
committed
compiler-capability-analysis: Add infrastructure for Clang's capability analysis
WIP A Clang version that supports -Wthread-safety-addressof is recommended, but not required: llvm/llvm-project#123063 Signed-off-by: Marco Elver <[email protected]>
1 parent 9093fae commit bde5ed2

File tree

5 files changed

+194
-7
lines changed

5 files changed

+194
-7
lines changed

Makefile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1082,6 +1082,7 @@ include-$(CONFIG_KCOV) += scripts/Makefile.kcov
10821082
include-$(CONFIG_RANDSTRUCT) += scripts/Makefile.randstruct
10831083
include-$(CONFIG_AUTOFDO_CLANG) += scripts/Makefile.autofdo
10841084
include-$(CONFIG_PROPELLER_CLANG) += scripts/Makefile.propeller
1085+
include-$(CONFIG_WARN_CAPABILITY_ANALYSIS) += scripts/Makefile.capability-analysis
10851086
include-$(CONFIG_GCC_PLUGINS) += scripts/Makefile.gcc-plugins
10861087

10871088
include $(addprefix $(srctree)/, $(include-y))

include/linux/compiler-capability-analysis.h

Lines changed: 156 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -6,26 +6,175 @@
66
#ifndef _LINUX_COMPILER_CAPABILITY_ANALYSIS_H
77
#define _LINUX_COMPILER_CAPABILITY_ANALYSIS_H
88

9+
#if defined(WARN_CAPABILITY_ANALYSIS)
10+
11+
/*
12+
* Tells the compiler to not do any capability analysis. Prefer
13+
* capability_unsafe(..) where possible.
14+
*/
15+
# define __no_capability_analysis __attribute__((no_thread_safety_analysis))
16+
17+
/*
18+
* The below attributes are used to define new capability types.
19+
*/
20+
# define __cap_type(name) __attribute__((capability(#name)))
21+
# define __acquires_cap(var) __attribute__((acquire_capability(var)))
22+
# define __acquires_shared_cap(var) __attribute__((acquire_shared_capability(var)))
23+
# define __try_acquires_cap(ret, var) __attribute__((try_acquire_capability(ret, var)))
24+
# define __try_acquires_shared_cap(ret, var) __attribute__((try_acquire_shared_capability(ret, var)))
25+
# define __releases_cap(var) __attribute__((release_capability(var)))
26+
# define __releases_shared_cap(var) __attribute__((release_shared_capability(var)))
27+
# define __asserts_cap(var) __attribute__((assert_capability(var)))
28+
# define __asserts_shared_cap(var) __attribute__((assert_shared_capability(var)))
29+
# define __returns_cap(var) __attribute__((lock_returned(var)))
30+
31+
/*
32+
* The below are used to annotate code being checked.
33+
*/
34+
# define __var_guarded_by(var) __attribute__((guarded_by(var)))
35+
# define __ref_guarded_by(var) __attribute__((pt_guarded_by(var)))
36+
# define __excludes_cap(var) __attribute__((locks_excluded(var)))
37+
# define __requires_cap(var) __attribute__((requires_capability(var)))
38+
# define __requires_shared_cap(var) __attribute__((requires_shared_capability(var)))
39+
40+
/*
41+
* Convenience helper to name a type with capability of the same name.
42+
* TODO: explain this
43+
*/
44+
# define struct_with_capability(name) \
45+
struct __cap_type(name) name; \
46+
static __always_inline void __acquire_cap(const struct name *var) \
47+
__attribute__((overloadable)) __no_capability_analysis __acquires_cap(var) { } \
48+
static __always_inline void __acquire_shared_cap(const struct name *var) \
49+
__attribute__((overloadable)) __no_capability_analysis __acquires_shared_cap(var) { } \
50+
static __always_inline bool __try_acquire_cap(const struct name *var, bool ret) \
51+
__attribute__((overloadable)) __no_capability_analysis __try_acquires_cap(1, var) \
52+
{ return ret; } \
53+
static __always_inline bool __try_acquire_shared_cap(const struct name *var, bool ret) \
54+
__attribute__((overloadable)) __no_capability_analysis __try_acquires_shared_cap(1, var) \
55+
{ return ret; } \
56+
static __always_inline void __release_cap(const struct name *var) \
57+
__attribute__((overloadable)) __no_capability_analysis __releases_cap(var) { } \
58+
static __always_inline void __release_shared_cap(const struct name *var) \
59+
__attribute__((overloadable)) __no_capability_analysis __releases_shared_cap(var) { } \
60+
static __always_inline void __assert_cap(const struct name *var) \
61+
__attribute__((overloadable)) __asserts_cap(var) { } \
62+
static __always_inline void __assert_shared_cap(const struct name *var) \
63+
__attribute__((overloadable)) __asserts_shared_cap(var) { } \
64+
struct name
65+
66+
/*
67+
* TODO:
68+
*/
69+
# define disable_capability_analysis() \
70+
__diag_push(); \
71+
__diag_ignore_all("-Wunknown-warning-option", "") \
72+
__diag_ignore_all("-Wthread-safety", "") \
73+
__diag_ignore_all("-Wthread-safety-addressof", "")
74+
75+
/*
76+
* TODO:
77+
*/
78+
# define enable_capability_analysis() __diag_pop()
79+
80+
#else /* !WARN_CAPABILITY_ANALYSIS */
81+
82+
# define __no_capability_analysis
83+
# define __cap_type(name)
84+
# define __acquires_cap(var)
85+
# define __acquires_shared_cap(var)
86+
# define __try_acquires_cap(ret, var)
87+
# define __try_acquires_shared_cap(ret, var)
88+
# define __releases_cap(var)
89+
# define __releases_shared_cap(var)
90+
# define __asserts_cap(var)
91+
# define __asserts_shared_cap(var)
92+
# define __returns_cap(var)
93+
# define __var_guarded_by(var)
94+
# define __ref_guarded_by(var)
95+
# define __excludes_cap(var)
96+
# define __requires_cap(var)
97+
# define __requires_shared_cap(var)
98+
# define __acquire_cap(var) do { } while (0)
99+
# define __acquire_shared_cap(var) do { } while (0)
100+
# define __try_acquire_cap(var, ret) (ret)
101+
# define __try_acquire_shared_cap(var, ret) (ret)
102+
# define __release_cap(var) do { } while (0)
103+
# define __release_shared_cap(var) do { } while (0)
104+
# define __assert_cap(var) do { (void)(var); } while (0)
105+
# define __assert_shared_cap(var) do { (void)(var); } while (0)
106+
# define struct_with_capability(name) struct name
107+
# define disable_capability_analysis()
108+
# define enable_capability_analysis()
109+
110+
#endif /* WARN_CAPABILITY_ANALYSIS */
111+
112+
/*
113+
* TODO: explain
114+
*
115+
* Works with any void or non-void expression.
116+
*/
117+
#define capability_unsafe(...) \
118+
({ \
119+
disable_capability_analysis(); \
120+
__VA_ARGS__; \
121+
enable_capability_analysis() \
122+
})
123+
124+
/*
125+
* An abstract global capability used as a token, but not backed by a real data
126+
* structure (linker error if accidentally used).
127+
*/
128+
#define token_capability(name) \
129+
struct_with_capability(__capability_##name) {}; \
130+
extern const struct __capability_##name *name
131+
/*
132+
* To define additional instances of the same token capability.
133+
*/
134+
#define token_capability_instance(cap, name) \
135+
extern const struct __capability_##cap *name
136+
137+
/*
138+
* Common keywords for static capability analysis. Both Clang's capability
139+
* analysis and Sparse's context tracking are currently supported.
140+
*/
9141
#ifdef __CHECKER__
10142

11143
/* Sparse context/lock checking support. */
12144
# define __must_hold(x) __attribute__((context(x,1,1)))
145+
# define __must_not_hold(x)
13146
# define __acquires(x) __attribute__((context(x,0,1)))
14147
# define __cond_acquires(x) __attribute__((context(x,0,-1)))
15148
# define __releases(x) __attribute__((context(x,1,0)))
16149
# define __acquire(x) __context__(x,1)
17150
# define __release(x) __context__(x,-1)
18151
# define __cond_acquire(x, c) ((c) ? ({ __acquire(x); 1; }) : 0)
152+
/* For Sparse, there's no distinction between exclusive and shared locks. */
153+
# define __must_hold_shared __must_hold
154+
# define __acquires_shared __acquires
155+
# define __cond_acquires_shared __cond_acquires
156+
# define __releases_shared __releases
157+
# define __acquire_shared __acquire
158+
# define __release_shared __release
159+
# define __cond_acquire_shared __cond_acquire
19160

20161
#else /* !__CHECKER__ */
21162

22-
# define __must_hold(x)
23-
# define __acquires(x)
24-
# define __cond_acquires(x)
25-
# define __releases(x)
26-
# define __acquire(x) (void)0
27-
# define __release(x) (void)0
28-
# define __cond_acquire(x, c) (c)
163+
# define __must_hold(x) __requires_cap(x)
164+
# define __must_not_hold(x) __excludes_cap(x)
165+
# define __acquires(x) __acquires_cap(x)
166+
# define __cond_acquires(x) __try_acquires_cap(1, x)
167+
# define __releases(x) __releases_cap(x)
168+
# define __acquire(x) __acquire_cap(x)
169+
# define __release(x) __release_cap(x)
170+
# define __cond_acquire(x, c) __try_acquire_cap(x, c)
171+
# define __must_hold_shared(x) __requires_shared_cap(x)
172+
# define __acquires_shared(x) __acquires_shared_cap(x)
173+
# define __cond_acquires_shared(x) __try_acquires_shared_cap(1, x)
174+
# define __releases_shared(x) __releases_shared_cap(x)
175+
# define __acquire_shared(x) __acquire_shared_cap(x)
176+
# define __release_shared(x) __release_shared_cap(x)
177+
# define __cond_acquire_shared(x, c) __try_acquire_shared_cap(x, c)
29178

30179
#endif /* __CHECKER__ */
31180

lib/Kconfig.debug

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -603,6 +603,28 @@ config DEBUG_FORCE_WEAK_PER_CPU
603603
To ensure that generic code follows the above rules, this
604604
option forces all percpu variables to be defined as weak.
605605

606+
config WARN_CAPABILITY_ANALYSIS
607+
bool "Compiler capability-analysis warnings"
608+
depends on CC_IS_CLANG && $(cc-option,-Wthread-safety -fexperimental-late-parse-attributes)
609+
default y
610+
help
611+
TODO: write me
612+
Clang's name of the feature ("Thread Safety Analysis") refers to
613+
the original name of the feature; it was later expanded to be a
614+
generic "Capability Analysis" framework.
615+
616+
Produces warnings by default. Select CONFIG_WERROR if you wish to
617+
turn these warnings into errors.
618+
619+
config WARN_CAPABILITY_ANALYSIS_ALL
620+
bool "Enable capability analysis for all source files"
621+
depends on WARN_CAPABILITY_ANALYSIS
622+
depends on EXPERT && !COMPILE_TEST
623+
help
624+
TODO
625+
626+
If unsure, say N.
627+
606628
endmenu # "Compiler options"
607629

608630
menu "Generic Kernel Debugging Instruments"
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
# SPDX-License-Identifier: GPL-2.0
2+
3+
export CFLAGS_CAPABILITY_ANALYSIS := -DWARN_CAPABILITY_ANALYSIS \
4+
-fexperimental-late-parse-attributes -Wthread-safety \
5+
$(call cc-option,-Wthread-safety-addressof)

scripts/Makefile.lib

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -191,6 +191,16 @@ _c_flags += $(if $(patsubst n%,, \
191191
-D__KCSAN_INSTRUMENT_BARRIERS__)
192192
endif
193193

194+
#
195+
# Enable capability analysis flags only where explicitly opted in.
196+
# (depends on variables CAPABILITY_ANALYSIS_obj.o, CAPABILITY_ANALYSIS)
197+
#
198+
ifeq ($(CONFIG_WARN_CAPABILITY_ANALYSIS),y)
199+
_c_flags += $(if $(patsubst n%,, \
200+
$(CAPABILITY_ANALYSIS_$(target-stem).o)$(CAPABILITY_ANALYSIS)$(if $(is-kernel-object),$(CONFIG_WARN_CAPABILITY_ANALYSIS_ALL))), \
201+
$(CFLAGS_CAPABILITY_ANALYSIS))
202+
endif
203+
194204
#
195205
# Enable AutoFDO build flags except some files or directories we don't want to
196206
# enable (depends on variables AUTOFDO_PROFILE_obj.o and AUTOFDO_PROFILE).

0 commit comments

Comments
 (0)