The Kernel Concurrency Sanitizer (KCSAN)
KCSAN is a dynamic race detector, which relies on compile-time instrumentation, and uses a watchpoint-based sampling approach to detect races. The feature was under development for quite some time and has already found legitimate bugs. Unfortunately it comes with a limitation, which was only understood late in the development cycle: It requires an up to date CLANG-11 compiler CLANG-11 is not yet released (scheduled for June), but it's the only compiler today which handles the kernel requirements and especially the annotations of functions to exclude them from KCSAN instrumentation correctly. These annotations really need to work so that low level entry code and especially int3 text poke handling can be completely isolated. A detailed discussion of the requirements and compiler issues can be found here: https://lore.kernel.org/lkml/CANpmjNMTsY_8241bS7=XAfqvZHFLrVEkv_uM4aDUWE_kh3Rvbw@mail.gmail.com/ We came to the conclusion that trying to work around compiler limitations and bugs again would end up in a major trainwreck, so requiring a working compiler seemed to be the best choice. For Continous Integration purposes the compiler restriction is manageable and that's where most xxSAN reports come from. For a change this limitation might make GCC people actually look at their bugs. Some issues with CSAN in GCC are 7 years old and one has been 'fixed' 3 years ago with a half baken solution which 'solved' the reported issue but not the underlying problem. The KCSAN developers also ponder to use a GCC plugin to become independent, but that's not something which will show up in a few days. Blocking KCSAN until wide spread compiler support is available is not a really good alternative because the continuous growth of lockless optimizations in the kernel demands proper tooling support. -----BEGIN PGP SIGNATURE----- iQJHBAABCgAxFiEEQp8+kY+LLUocC4bMphj1TA10mKEFAl7im98THHRnbHhAbGlu dXRyb25peC5kZQAKCRCmGPVMDXSYoQ3xD/9+q87OmwnyoRTs6O3GDDbWZYoJGolh rctDOAYW8RSS73Fiw23z8hKlLl9tJCya6/X8Q9qoonB1YeIEPPRVj5HJWAMUNEIs YgjlZJFmh+mnbP/KQFctm3AWpoX8kqt3ncqj6zG72oQ9qKui691BY/2NmGVSLxUV DqtUYSKmi51XEQtZuXRuHEf3zBxoyeD43DaSCdJAXd6f5O2X7tmrWDuazHVeKzHV lhijvkyBvGMWvPg0IBrXkkLmeOvS0++MTGm3o+L72XF6nWpzTkcV7N0E9GEDFg45 zwcidRVKD5d/1DoU5Tos96rCJpBEGh/wimlu0z14mcZpNiJgRQH5rzVEO9Y14UcP KL9FgRrb5dFw7yfX2zRQ070OFJ4AEDBMK0o5Lbu/QO5KLkvFkqnuWlQfmmtZJWCW DTRw/FgUgU7lvyPjRrao6HBvwy+yTb0u9K5seCOTRkuepR9nPJs0710pFiBsNCfV RY3cyggNBipAzgBOgLxixnq9+rHt70ton6S8Gijxpvt0dGGfO8k0wuEhFtA4zKrQ 6HGK+pidxnoVdEgyQZhS+qzMMkyiUL0FXdaGJ2IX+/DC+Ij1UrUPjZBn7v25M0hQ ESkvxWKCn7snH4/NJsNxqCV1zyEc3zAW/WvLJUc9I7H8zPwtVvKWPrKEMzrJJ5bA aneySilbRxBFUg== =iplm -----END PGP SIGNATURE----- Merge tag 'locking-kcsan-2020-06-11' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip Pull the Kernel Concurrency Sanitizer from Thomas Gleixner: "The Kernel Concurrency Sanitizer (KCSAN) is a dynamic race detector, which relies on compile-time instrumentation, and uses a watchpoint-based sampling approach to detect races. The feature was under development for quite some time and has already found legitimate bugs. Unfortunately it comes with a limitation, which was only understood late in the development cycle: It requires an up to date CLANG-11 compiler CLANG-11 is not yet released (scheduled for June), but it's the only compiler today which handles the kernel requirements and especially the annotations of functions to exclude them from KCSAN instrumentation correctly. These annotations really need to work so that low level entry code and especially int3 text poke handling can be completely isolated. A detailed discussion of the requirements and compiler issues can be found here: https://lore.kernel.org/lkml/CANpmjNMTsY_8241bS7=XAfqvZHFLrVEkv_uM4aDUWE_kh3Rvbw@mail.gmail.com/ We came to the conclusion that trying to work around compiler limitations and bugs again would end up in a major trainwreck, so requiring a working compiler seemed to be the best choice. For Continous Integration purposes the compiler restriction is manageable and that's where most xxSAN reports come from. For a change this limitation might make GCC people actually look at their bugs. Some issues with CSAN in GCC are 7 years old and one has been 'fixed' 3 years ago with a half baken solution which 'solved' the reported issue but not the underlying problem. The KCSAN developers also ponder to use a GCC plugin to become independent, but that's not something which will show up in a few days. Blocking KCSAN until wide spread compiler support is available is not a really good alternative because the continuous growth of lockless optimizations in the kernel demands proper tooling support" * tag 'locking-kcsan-2020-06-11' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip: (76 commits) compiler_types.h, kasan: Use __SANITIZE_ADDRESS__ instead of CONFIG_KASAN to decide inlining compiler.h: Move function attributes to compiler_types.h compiler.h: Avoid nested statement expression in data_race() compiler.h: Remove data_race() and unnecessary checks from {READ,WRITE}_ONCE() kcsan: Update Documentation to change supported compilers kcsan: Remove 'noinline' from __no_kcsan_or_inline kcsan: Pass option tsan-instrument-read-before-write to Clang kcsan: Support distinguishing volatile accesses kcsan: Restrict supported compilers kcsan: Avoid inserting __tsan_func_entry/exit if possible ubsan, kcsan: Don't combine sanitizer with kcov on clang objtool, kcsan: Add kcsan_disable_current() and kcsan_enable_current_nowarn() kcsan: Add __kcsan_{enable,disable}_current() variants checkpatch: Warn about data_race() without comment kcsan: Use GFP_ATOMIC under spin lock Improve KCSAN documentation a bit kcsan: Make reporting aware of KCSAN tests kcsan: Fix function matching in report kcsan: Change data_race() to no longer require marking racing accesses kcsan: Move kcsan_{disable,enable}_current() to kcsan-checks.h ...
This commit is contained in:
commit
b791d1bdf9
|
@ -21,6 +21,7 @@ whole; patches welcome!
|
|||
kasan
|
||||
ubsan
|
||||
kmemleak
|
||||
kcsan
|
||||
gdb-kernel-debugging
|
||||
kgdb
|
||||
kselftest
|
||||
|
|
|
@ -0,0 +1,321 @@
|
|||
The Kernel Concurrency Sanitizer (KCSAN)
|
||||
========================================
|
||||
|
||||
The Kernel Concurrency Sanitizer (KCSAN) is a dynamic race detector, which
|
||||
relies on compile-time instrumentation, and uses a watchpoint-based sampling
|
||||
approach to detect races. KCSAN's primary purpose is to detect `data races`_.
|
||||
|
||||
Usage
|
||||
-----
|
||||
|
||||
KCSAN requires Clang version 11 or later.
|
||||
|
||||
To enable KCSAN configure the kernel with::
|
||||
|
||||
CONFIG_KCSAN = y
|
||||
|
||||
KCSAN provides several other configuration options to customize behaviour (see
|
||||
the respective help text in ``lib/Kconfig.kcsan`` for more info).
|
||||
|
||||
Error reports
|
||||
~~~~~~~~~~~~~
|
||||
|
||||
A typical data race report looks like this::
|
||||
|
||||
==================================================================
|
||||
BUG: KCSAN: data-race in generic_permission / kernfs_refresh_inode
|
||||
|
||||
write to 0xffff8fee4c40700c of 4 bytes by task 175 on cpu 4:
|
||||
kernfs_refresh_inode+0x70/0x170
|
||||
kernfs_iop_permission+0x4f/0x90
|
||||
inode_permission+0x190/0x200
|
||||
link_path_walk.part.0+0x503/0x8e0
|
||||
path_lookupat.isra.0+0x69/0x4d0
|
||||
filename_lookup+0x136/0x280
|
||||
user_path_at_empty+0x47/0x60
|
||||
vfs_statx+0x9b/0x130
|
||||
__do_sys_newlstat+0x50/0xb0
|
||||
__x64_sys_newlstat+0x37/0x50
|
||||
do_syscall_64+0x85/0x260
|
||||
entry_SYSCALL_64_after_hwframe+0x44/0xa9
|
||||
|
||||
read to 0xffff8fee4c40700c of 4 bytes by task 166 on cpu 6:
|
||||
generic_permission+0x5b/0x2a0
|
||||
kernfs_iop_permission+0x66/0x90
|
||||
inode_permission+0x190/0x200
|
||||
link_path_walk.part.0+0x503/0x8e0
|
||||
path_lookupat.isra.0+0x69/0x4d0
|
||||
filename_lookup+0x136/0x280
|
||||
user_path_at_empty+0x47/0x60
|
||||
do_faccessat+0x11a/0x390
|
||||
__x64_sys_access+0x3c/0x50
|
||||
do_syscall_64+0x85/0x260
|
||||
entry_SYSCALL_64_after_hwframe+0x44/0xa9
|
||||
|
||||
Reported by Kernel Concurrency Sanitizer on:
|
||||
CPU: 6 PID: 166 Comm: systemd-journal Not tainted 5.3.0-rc7+ #1
|
||||
Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS 1.12.0-1 04/01/2014
|
||||
==================================================================
|
||||
|
||||
The header of the report provides a short summary of the functions involved in
|
||||
the race. It is followed by the access types and stack traces of the 2 threads
|
||||
involved in the data race.
|
||||
|
||||
The other less common type of data race report looks like this::
|
||||
|
||||
==================================================================
|
||||
BUG: KCSAN: data-race in e1000_clean_rx_irq+0x551/0xb10
|
||||
|
||||
race at unknown origin, with read to 0xffff933db8a2ae6c of 1 bytes by interrupt on cpu 0:
|
||||
e1000_clean_rx_irq+0x551/0xb10
|
||||
e1000_clean+0x533/0xda0
|
||||
net_rx_action+0x329/0x900
|
||||
__do_softirq+0xdb/0x2db
|
||||
irq_exit+0x9b/0xa0
|
||||
do_IRQ+0x9c/0xf0
|
||||
ret_from_intr+0x0/0x18
|
||||
default_idle+0x3f/0x220
|
||||
arch_cpu_idle+0x21/0x30
|
||||
do_idle+0x1df/0x230
|
||||
cpu_startup_entry+0x14/0x20
|
||||
rest_init+0xc5/0xcb
|
||||
arch_call_rest_init+0x13/0x2b
|
||||
start_kernel+0x6db/0x700
|
||||
|
||||
Reported by Kernel Concurrency Sanitizer on:
|
||||
CPU: 0 PID: 0 Comm: swapper/0 Not tainted 5.3.0-rc7+ #2
|
||||
Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS 1.12.0-1 04/01/2014
|
||||
==================================================================
|
||||
|
||||
This report is generated where it was not possible to determine the other
|
||||
racing thread, but a race was inferred due to the data value of the watched
|
||||
memory location having changed. These can occur either due to missing
|
||||
instrumentation or e.g. DMA accesses. These reports will only be generated if
|
||||
``CONFIG_KCSAN_REPORT_RACE_UNKNOWN_ORIGIN=y`` (selected by default).
|
||||
|
||||
Selective analysis
|
||||
~~~~~~~~~~~~~~~~~~
|
||||
|
||||
It may be desirable to disable data race detection for specific accesses,
|
||||
functions, compilation units, or entire subsystems. For static blacklisting,
|
||||
the below options are available:
|
||||
|
||||
* KCSAN understands the ``data_race(expr)`` annotation, which tells KCSAN that
|
||||
any data races due to accesses in ``expr`` should be ignored and resulting
|
||||
behaviour when encountering a data race is deemed safe.
|
||||
|
||||
* Disabling data race detection for entire functions can be accomplished by
|
||||
using the function attribute ``__no_kcsan``::
|
||||
|
||||
__no_kcsan
|
||||
void foo(void) {
|
||||
...
|
||||
|
||||
To dynamically limit for which functions to generate reports, see the
|
||||
`DebugFS interface`_ blacklist/whitelist feature.
|
||||
|
||||
For ``__always_inline`` functions, replace ``__always_inline`` with
|
||||
``__no_kcsan_or_inline`` (which implies ``__always_inline``)::
|
||||
|
||||
static __no_kcsan_or_inline void foo(void) {
|
||||
...
|
||||
|
||||
* To disable data race detection for a particular compilation unit, add to the
|
||||
``Makefile``::
|
||||
|
||||
KCSAN_SANITIZE_file.o := n
|
||||
|
||||
* To disable data race detection for all compilation units listed in a
|
||||
``Makefile``, add to the respective ``Makefile``::
|
||||
|
||||
KCSAN_SANITIZE := n
|
||||
|
||||
Furthermore, it is possible to tell KCSAN to show or hide entire classes of
|
||||
data races, depending on preferences. These can be changed via the following
|
||||
Kconfig options:
|
||||
|
||||
* ``CONFIG_KCSAN_REPORT_VALUE_CHANGE_ONLY``: If enabled and a conflicting write
|
||||
is observed via a watchpoint, but the data value of the memory location was
|
||||
observed to remain unchanged, do not report the data race.
|
||||
|
||||
* ``CONFIG_KCSAN_ASSUME_PLAIN_WRITES_ATOMIC``: Assume that plain aligned writes
|
||||
up to word size are atomic by default. Assumes that such writes are not
|
||||
subject to unsafe compiler optimizations resulting in data races. The option
|
||||
causes KCSAN to not report data races due to conflicts where the only plain
|
||||
accesses are aligned writes up to word size.
|
||||
|
||||
DebugFS interface
|
||||
~~~~~~~~~~~~~~~~~
|
||||
|
||||
The file ``/sys/kernel/debug/kcsan`` provides the following interface:
|
||||
|
||||
* Reading ``/sys/kernel/debug/kcsan`` returns various runtime statistics.
|
||||
|
||||
* Writing ``on`` or ``off`` to ``/sys/kernel/debug/kcsan`` allows turning KCSAN
|
||||
on or off, respectively.
|
||||
|
||||
* Writing ``!some_func_name`` to ``/sys/kernel/debug/kcsan`` adds
|
||||
``some_func_name`` to the report filter list, which (by default) blacklists
|
||||
reporting data races where either one of the top stackframes are a function
|
||||
in the list.
|
||||
|
||||
* Writing either ``blacklist`` or ``whitelist`` to ``/sys/kernel/debug/kcsan``
|
||||
changes the report filtering behaviour. For example, the blacklist feature
|
||||
can be used to silence frequently occurring data races; the whitelist feature
|
||||
can help with reproduction and testing of fixes.
|
||||
|
||||
Tuning performance
|
||||
~~~~~~~~~~~~~~~~~~
|
||||
|
||||
Core parameters that affect KCSAN's overall performance and bug detection
|
||||
ability are exposed as kernel command-line arguments whose defaults can also be
|
||||
changed via the corresponding Kconfig options.
|
||||
|
||||
* ``kcsan.skip_watch`` (``CONFIG_KCSAN_SKIP_WATCH``): Number of per-CPU memory
|
||||
operations to skip, before another watchpoint is set up. Setting up
|
||||
watchpoints more frequently will result in the likelihood of races to be
|
||||
observed to increase. This parameter has the most significant impact on
|
||||
overall system performance and race detection ability.
|
||||
|
||||
* ``kcsan.udelay_task`` (``CONFIG_KCSAN_UDELAY_TASK``): For tasks, the
|
||||
microsecond delay to stall execution after a watchpoint has been set up.
|
||||
Larger values result in the window in which we may observe a race to
|
||||
increase.
|
||||
|
||||
* ``kcsan.udelay_interrupt`` (``CONFIG_KCSAN_UDELAY_INTERRUPT``): For
|
||||
interrupts, the microsecond delay to stall execution after a watchpoint has
|
||||
been set up. Interrupts have tighter latency requirements, and their delay
|
||||
should generally be smaller than the one chosen for tasks.
|
||||
|
||||
They may be tweaked at runtime via ``/sys/module/kcsan/parameters/``.
|
||||
|
||||
Data Races
|
||||
----------
|
||||
|
||||
In an execution, two memory accesses form a *data race* if they *conflict*,
|
||||
they happen concurrently in different threads, and at least one of them is a
|
||||
*plain access*; they *conflict* if both access the same memory location, and at
|
||||
least one is a write. For a more thorough discussion and definition, see `"Plain
|
||||
Accesses and Data Races" in the LKMM`_.
|
||||
|
||||
.. _"Plain Accesses and Data Races" in the LKMM: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/tools/memory-model/Documentation/explanation.txt#n1922
|
||||
|
||||
Relationship with the Linux-Kernel Memory Consistency Model (LKMM)
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
The LKMM defines the propagation and ordering rules of various memory
|
||||
operations, which gives developers the ability to reason about concurrent code.
|
||||
Ultimately this allows to determine the possible executions of concurrent code,
|
||||
and if that code is free from data races.
|
||||
|
||||
KCSAN is aware of *marked atomic operations* (``READ_ONCE``, ``WRITE_ONCE``,
|
||||
``atomic_*``, etc.), but is oblivious of any ordering guarantees and simply
|
||||
assumes that memory barriers are placed correctly. In other words, KCSAN
|
||||
assumes that as long as a plain access is not observed to race with another
|
||||
conflicting access, memory operations are correctly ordered.
|
||||
|
||||
This means that KCSAN will not report *potential* data races due to missing
|
||||
memory ordering. Developers should therefore carefully consider the required
|
||||
memory ordering requirements that remain unchecked. If, however, missing
|
||||
memory ordering (that is observable with a particular compiler and
|
||||
architecture) leads to an observable data race (e.g. entering a critical
|
||||
section erroneously), KCSAN would report the resulting data race.
|
||||
|
||||
Race Detection Beyond Data Races
|
||||
--------------------------------
|
||||
|
||||
For code with complex concurrency design, race-condition bugs may not always
|
||||
manifest as data races. Race conditions occur if concurrently executing
|
||||
operations result in unexpected system behaviour. On the other hand, data races
|
||||
are defined at the C-language level. The following macros can be used to check
|
||||
properties of concurrent code where bugs would not manifest as data races.
|
||||
|
||||
.. kernel-doc:: include/linux/kcsan-checks.h
|
||||
:functions: ASSERT_EXCLUSIVE_WRITER ASSERT_EXCLUSIVE_WRITER_SCOPED
|
||||
ASSERT_EXCLUSIVE_ACCESS ASSERT_EXCLUSIVE_ACCESS_SCOPED
|
||||
ASSERT_EXCLUSIVE_BITS
|
||||
|
||||
Implementation Details
|
||||
----------------------
|
||||
|
||||
KCSAN relies on observing that two accesses happen concurrently. Crucially, we
|
||||
want to (a) increase the chances of observing races (especially for races that
|
||||
manifest rarely), and (b) be able to actually observe them. We can accomplish
|
||||
(a) by injecting various delays, and (b) by using address watchpoints (or
|
||||
breakpoints).
|
||||
|
||||
If we deliberately stall a memory access, while we have a watchpoint for its
|
||||
address set up, and then observe the watchpoint to fire, two accesses to the
|
||||
same address just raced. Using hardware watchpoints, this is the approach taken
|
||||
in `DataCollider
|
||||
<http://usenix.org/legacy/events/osdi10/tech/full_papers/Erickson.pdf>`_.
|
||||
Unlike DataCollider, KCSAN does not use hardware watchpoints, but instead
|
||||
relies on compiler instrumentation and "soft watchpoints".
|
||||
|
||||
In KCSAN, watchpoints are implemented using an efficient encoding that stores
|
||||
access type, size, and address in a long; the benefits of using "soft
|
||||
watchpoints" are portability and greater flexibility. KCSAN then relies on the
|
||||
compiler instrumenting plain accesses. For each instrumented plain access:
|
||||
|
||||
1. Check if a matching watchpoint exists; if yes, and at least one access is a
|
||||
write, then we encountered a racing access.
|
||||
|
||||
2. Periodically, if no matching watchpoint exists, set up a watchpoint and
|
||||
stall for a small randomized delay.
|
||||
|
||||
3. Also check the data value before the delay, and re-check the data value
|
||||
after delay; if the values mismatch, we infer a race of unknown origin.
|
||||
|
||||
To detect data races between plain and marked accesses, KCSAN also annotates
|
||||
marked accesses, but only to check if a watchpoint exists; i.e. KCSAN never
|
||||
sets up a watchpoint on marked accesses. By never setting up watchpoints for
|
||||
marked operations, if all accesses to a variable that is accessed concurrently
|
||||
are properly marked, KCSAN will never trigger a watchpoint and therefore never
|
||||
report the accesses.
|
||||
|
||||
Key Properties
|
||||
~~~~~~~~~~~~~~
|
||||
|
||||
1. **Memory Overhead:** The overall memory overhead is only a few MiB
|
||||
depending on configuration. The current implementation uses a small array of
|
||||
longs to encode watchpoint information, which is negligible.
|
||||
|
||||
2. **Performance Overhead:** KCSAN's runtime aims to be minimal, using an
|
||||
efficient watchpoint encoding that does not require acquiring any shared
|
||||
locks in the fast-path. For kernel boot on a system with 8 CPUs:
|
||||
|
||||
- 5.0x slow-down with the default KCSAN config;
|
||||
- 2.8x slow-down from runtime fast-path overhead only (set very large
|
||||
``KCSAN_SKIP_WATCH`` and unset ``KCSAN_SKIP_WATCH_RANDOMIZE``).
|
||||
|
||||
3. **Annotation Overheads:** Minimal annotations are required outside the KCSAN
|
||||
runtime. As a result, maintenance overheads are minimal as the kernel
|
||||
evolves.
|
||||
|
||||
4. **Detects Racy Writes from Devices:** Due to checking data values upon
|
||||
setting up watchpoints, racy writes from devices can also be detected.
|
||||
|
||||
5. **Memory Ordering:** KCSAN is *not* explicitly aware of the LKMM's ordering
|
||||
rules; this may result in missed data races (false negatives).
|
||||
|
||||
6. **Analysis Accuracy:** For observed executions, due to using a sampling
|
||||
strategy, the analysis is *unsound* (false negatives possible), but aims to
|
||||
be complete (no false positives).
|
||||
|
||||
Alternatives Considered
|
||||
-----------------------
|
||||
|
||||
An alternative data race detection approach for the kernel can be found in the
|
||||
`Kernel Thread Sanitizer (KTSAN) <https://github.com/google/ktsan/wiki>`_.
|
||||
KTSAN is a happens-before data race detector, which explicitly establishes the
|
||||
happens-before order between memory operations, which can then be used to
|
||||
determine data races as defined in `Data Races`_.
|
||||
|
||||
To build a correct happens-before relation, KTSAN must be aware of all ordering
|
||||
rules of the LKMM and synchronization primitives. Unfortunately, any omission
|
||||
leads to large numbers of false positives, which is especially detrimental in
|
||||
the context of the kernel which includes numerous custom synchronization
|
||||
mechanisms. To track the happens-before relation, KTSAN's implementation
|
||||
requires metadata for each memory location (shadow memory), which for each page
|
||||
corresponds to 4 pages of shadow memory, and can translate into overhead of
|
||||
tens of GiB on a large system.
|
11
MAINTAINERS
11
MAINTAINERS
|
@ -9305,6 +9305,17 @@ F: Documentation/kbuild/kconfig*
|
|||
F: scripts/Kconfig.include
|
||||
F: scripts/kconfig/
|
||||
|
||||
KCSAN
|
||||
M: Marco Elver <elver@google.com>
|
||||
R: Dmitry Vyukov <dvyukov@google.com>
|
||||
L: kasan-dev@googlegroups.com
|
||||
S: Maintained
|
||||
F: Documentation/dev-tools/kcsan.rst
|
||||
F: include/linux/kcsan*.h
|
||||
F: kernel/kcsan/
|
||||
F: lib/Kconfig.kcsan
|
||||
F: scripts/Makefile.kcsan
|
||||
|
||||
KDUMP
|
||||
M: Dave Young <dyoung@redhat.com>
|
||||
M: Baoquan He <bhe@redhat.com>
|
||||
|
|
3
Makefile
3
Makefile
|
@ -531,7 +531,7 @@ export KBUILD_HOSTCXXFLAGS KBUILD_HOSTLDFLAGS KBUILD_HOSTLDLIBS LDFLAGS_MODULE
|
|||
|
||||
export KBUILD_CPPFLAGS NOSTDINC_FLAGS LINUXINCLUDE OBJCOPYFLAGS KBUILD_LDFLAGS
|
||||
export KBUILD_CFLAGS CFLAGS_KERNEL CFLAGS_MODULE
|
||||
export CFLAGS_KASAN CFLAGS_KASAN_NOSANITIZE CFLAGS_UBSAN
|
||||
export CFLAGS_KASAN CFLAGS_KASAN_NOSANITIZE CFLAGS_UBSAN CFLAGS_KCSAN
|
||||
export KBUILD_AFLAGS AFLAGS_KERNEL AFLAGS_MODULE
|
||||
export KBUILD_AFLAGS_MODULE KBUILD_CFLAGS_MODULE KBUILD_LDFLAGS_MODULE
|
||||
export KBUILD_AFLAGS_KERNEL KBUILD_CFLAGS_KERNEL
|
||||
|
@ -965,6 +965,7 @@ endif
|
|||
include scripts/Makefile.kasan
|
||||
include scripts/Makefile.extrawarn
|
||||
include scripts/Makefile.ubsan
|
||||
include scripts/Makefile.kcsan
|
||||
|
||||
# Add user supplied CPPFLAGS, AFLAGS and CFLAGS as the last assignments
|
||||
KBUILD_CPPFLAGS += $(KCPPFLAGS)
|
||||
|
|
|
@ -233,6 +233,7 @@ config X86
|
|||
select THREAD_INFO_IN_TASK
|
||||
select USER_STACKTRACE_SUPPORT
|
||||
select VIRT_TO_BUS
|
||||
select HAVE_ARCH_KCSAN if X86_64
|
||||
select X86_FEATURE_NAMES if PROC_FS
|
||||
select PROC_PID_ARCH_STATUS if PROC_FS
|
||||
imply IMA_SECURE_AND_OR_TRUSTED_BOOT if EFI
|
||||
|
|
|
@ -9,7 +9,9 @@
|
|||
# Changed by many, many contributors over the years.
|
||||
#
|
||||
|
||||
# Sanitizer runtimes are unavailable and cannot be linked for early boot code.
|
||||
KASAN_SANITIZE := n
|
||||
KCSAN_SANITIZE := n
|
||||
OBJECT_FILES_NON_STANDARD := y
|
||||
|
||||
# Kernel does not boot with kcov instrumentation here.
|
||||
|
|
|
@ -17,7 +17,9 @@
|
|||
# (see scripts/Makefile.lib size_append)
|
||||
# compressed vmlinux.bin.all + u32 size of vmlinux.bin.all
|
||||
|
||||
# Sanitizer runtimes are unavailable and cannot be linked for early boot code.
|
||||
KASAN_SANITIZE := n
|
||||
KCSAN_SANITIZE := n
|
||||
OBJECT_FILES_NON_STANDARD := y
|
||||
|
||||
# Prevents link failures: __sanitizer_cov_trace_pc() is not linked in.
|
||||
|
|
|
@ -10,8 +10,11 @@ ARCH_REL_TYPE_ABS += R_386_GLOB_DAT|R_386_JMP_SLOT|R_386_RELATIVE
|
|||
include $(srctree)/lib/vdso/Makefile
|
||||
|
||||
KBUILD_CFLAGS += $(DISABLE_LTO)
|
||||
|
||||
# Sanitizer runtimes are unavailable and cannot be linked here.
|
||||
KASAN_SANITIZE := n
|
||||
UBSAN_SANITIZE := n
|
||||
KCSAN_SANITIZE := n
|
||||
OBJECT_FILES_NON_STANDARD := y
|
||||
|
||||
# Prevents link failures: __sanitizer_cov_trace_pc() is not linked in.
|
||||
|
@ -29,6 +32,9 @@ vobjs32-y += vdso32/vclock_gettime.o
|
|||
|
||||
# files to link into kernel
|
||||
obj-y += vma.o
|
||||
KASAN_SANITIZE_vma.o := y
|
||||
UBSAN_SANITIZE_vma.o := y
|
||||
KCSAN_SANITIZE_vma.o := y
|
||||
OBJECT_FILES_NON_STANDARD_vma.o := n
|
||||
|
||||
# vDSO images to build
|
||||
|
|
|
@ -201,8 +201,12 @@ arch_test_and_change_bit(long nr, volatile unsigned long *addr)
|
|||
return GEN_BINARY_RMWcc(LOCK_PREFIX __ASM_SIZE(btc), *addr, c, "Ir", nr);
|
||||
}
|
||||
|
||||
static __always_inline bool constant_test_bit(long nr, const volatile unsigned long *addr)
|
||||
static __no_kcsan_or_inline bool constant_test_bit(long nr, const volatile unsigned long *addr)
|
||||
{
|
||||
/*
|
||||
* Because this is a plain access, we need to disable KCSAN here to
|
||||
* avoid double instrumentation via instrumented bitops.
|
||||
*/
|
||||
return ((1UL << (nr & (BITS_PER_LONG-1))) &
|
||||
(addr[nr >> _BITOPS_LONG_SHIFT])) != 0;
|
||||
}
|
||||
|
|
|
@ -28,6 +28,10 @@ KASAN_SANITIZE_dumpstack_$(BITS).o := n
|
|||
KASAN_SANITIZE_stacktrace.o := n
|
||||
KASAN_SANITIZE_paravirt.o := n
|
||||
|
||||
# With some compiler versions the generated code results in boot hangs, caused
|
||||
# by several compilation units. To be safe, disable all instrumentation.
|
||||
KCSAN_SANITIZE := n
|
||||
|
||||
OBJECT_FILES_NON_STANDARD_test_nx.o := y
|
||||
OBJECT_FILES_NON_STANDARD_paravirt_patch.o := y
|
||||
|
||||
|
|
|
@ -13,6 +13,9 @@ endif
|
|||
KCOV_INSTRUMENT_common.o := n
|
||||
KCOV_INSTRUMENT_perf_event.o := n
|
||||
|
||||
# As above, instrumenting secondary CPU boot code causes boot hangs.
|
||||
KCSAN_SANITIZE_common.o := n
|
||||
|
||||
# Make sure load_percpu_segment has no stackprotector
|
||||
nostackp := $(call cc-option, -fno-stack-protector)
|
||||
CFLAGS_common.o := $(nostackp)
|
||||
|
|
|
@ -991,7 +991,15 @@ void __init e820__reserve_setup_data(void)
|
|||
while (pa_data) {
|
||||
data = early_memremap(pa_data, sizeof(*data));
|
||||
e820__range_update(pa_data, sizeof(*data)+data->len, E820_TYPE_RAM, E820_TYPE_RESERVED_KERN);
|
||||
e820__range_update_kexec(pa_data, sizeof(*data)+data->len, E820_TYPE_RAM, E820_TYPE_RESERVED_KERN);
|
||||
|
||||
/*
|
||||
* SETUP_EFI is supplied by kexec and does not need to be
|
||||
* reserved.
|
||||
*/
|
||||
if (data->type != SETUP_EFI)
|
||||
e820__range_update_kexec(pa_data,
|
||||
sizeof(*data) + data->len,
|
||||
E820_TYPE_RAM, E820_TYPE_RESERVED_KERN);
|
||||
|
||||
if (data->type == SETUP_INDIRECT &&
|
||||
((struct setup_indirect *)data->data)->type != SETUP_INDIRECT) {
|
||||
|
|
|
@ -6,10 +6,19 @@
|
|||
# Produces uninteresting flaky coverage.
|
||||
KCOV_INSTRUMENT_delay.o := n
|
||||
|
||||
# KCSAN uses udelay for introducing watchpoint delay; avoid recursion.
|
||||
KCSAN_SANITIZE_delay.o := n
|
||||
ifdef CONFIG_KCSAN
|
||||
# In case KCSAN+lockdep+ftrace are enabled, disable ftrace for delay.o to avoid
|
||||
# lockdep -> [other libs] -> KCSAN -> udelay -> ftrace -> lockdep recursion.
|
||||
CFLAGS_REMOVE_delay.o = $(CC_FLAGS_FTRACE)
|
||||
endif
|
||||
|
||||
# Early boot use of cmdline; don't instrument it
|
||||
ifdef CONFIG_AMD_MEM_ENCRYPT
|
||||
KCOV_INSTRUMENT_cmdline.o := n
|
||||
KASAN_SANITIZE_cmdline.o := n
|
||||
KCSAN_SANITIZE_cmdline.o := n
|
||||
|
||||
ifdef CONFIG_FUNCTION_TRACER
|
||||
CFLAGS_REMOVE_cmdline.o = -pg
|
||||
|
|
|
@ -7,6 +7,10 @@ KCOV_INSTRUMENT_mem_encrypt_identity.o := n
|
|||
KASAN_SANITIZE_mem_encrypt.o := n
|
||||
KASAN_SANITIZE_mem_encrypt_identity.o := n
|
||||
|
||||
# Disable KCSAN entirely, because otherwise we get warnings that some functions
|
||||
# reference __initdata sections.
|
||||
KCSAN_SANITIZE := n
|
||||
|
||||
ifdef CONFIG_FUNCTION_TRACER
|
||||
CFLAGS_REMOVE_mem_encrypt.o = -pg
|
||||
CFLAGS_REMOVE_mem_encrypt_identity.o = -pg
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
purgatory.chk
|
|
@ -14,10 +14,18 @@ $(obj)/sha256.o: $(srctree)/lib/crypto/sha256.c FORCE
|
|||
|
||||
CFLAGS_sha256.o := -D__DISABLE_EXPORTS
|
||||
|
||||
LDFLAGS_purgatory.ro := -e purgatory_start -r --no-undefined -nostdlib -z nodefaultlib
|
||||
targets += purgatory.ro
|
||||
# When linking purgatory.ro with -r unresolved symbols are not checked,
|
||||
# also link a purgatory.chk binary without -r to check for unresolved symbols.
|
||||
PURGATORY_LDFLAGS := -e purgatory_start -nostdlib -z nodefaultlib
|
||||
LDFLAGS_purgatory.ro := -r $(PURGATORY_LDFLAGS)
|
||||
LDFLAGS_purgatory.chk := $(PURGATORY_LDFLAGS)
|
||||
targets += purgatory.ro purgatory.chk
|
||||
|
||||
# Sanitizer, etc. runtimes are unavailable and cannot be linked here.
|
||||
GCOV_PROFILE := n
|
||||
KASAN_SANITIZE := n
|
||||
UBSAN_SANITIZE := n
|
||||
KCSAN_SANITIZE := n
|
||||
KCOV_INSTRUMENT := n
|
||||
|
||||
# These are adjustments to the compiler flags used for objects that
|
||||
|
@ -25,7 +33,7 @@ KCOV_INSTRUMENT := n
|
|||
|
||||
PURGATORY_CFLAGS_REMOVE := -mcmodel=kernel
|
||||
PURGATORY_CFLAGS := -mcmodel=large -ffreestanding -fno-zero-initialized-in-bss
|
||||
PURGATORY_CFLAGS += $(DISABLE_STACKLEAK_PLUGIN)
|
||||
PURGATORY_CFLAGS += $(DISABLE_STACKLEAK_PLUGIN) -DDISABLE_BRANCH_PROFILING
|
||||
|
||||
# Default KBUILD_CFLAGS can have -pg option set when FTRACE is enabled. That
|
||||
# in turn leaves some undefined symbols like __fentry__ in purgatory and not
|
||||
|
@ -58,12 +66,15 @@ CFLAGS_string.o += $(PURGATORY_CFLAGS)
|
|||
$(obj)/purgatory.ro: $(PURGATORY_OBJS) FORCE
|
||||
$(call if_changed,ld)
|
||||
|
||||
$(obj)/purgatory.chk: $(obj)/purgatory.ro FORCE
|
||||
$(call if_changed,ld)
|
||||
|
||||
targets += kexec-purgatory.c
|
||||
|
||||
quiet_cmd_bin2c = BIN2C $@
|
||||
cmd_bin2c = $(objtree)/scripts/bin2c kexec_purgatory < $< > $@
|
||||
|
||||
$(obj)/kexec-purgatory.c: $(obj)/purgatory.ro FORCE
|
||||
$(obj)/kexec-purgatory.c: $(obj)/purgatory.ro $(obj)/purgatory.chk FORCE
|
||||
$(call if_changed,bin2c)
|
||||
|
||||
obj-$(CONFIG_KEXEC_FILE) += kexec-purgatory.o
|
||||
|
|
|
@ -6,7 +6,10 @@
|
|||
# for more details.
|
||||
#
|
||||
#
|
||||
|
||||
# Sanitizer runtimes are unavailable and cannot be linked here.
|
||||
KASAN_SANITIZE := n
|
||||
KCSAN_SANITIZE := n
|
||||
OBJECT_FILES_NON_STANDARD := y
|
||||
|
||||
subdir- := rm
|
||||
|
|
|
@ -6,7 +6,10 @@
|
|||
# for more details.
|
||||
#
|
||||
#
|
||||
|
||||
# Sanitizer runtimes are unavailable and cannot be linked here.
|
||||
KASAN_SANITIZE := n
|
||||
KCSAN_SANITIZE := n
|
||||
OBJECT_FILES_NON_STANDARD := y
|
||||
|
||||
# Prevents link failures: __sanitizer_cov_trace_pc() is not linked in.
|
||||
|
|
|
@ -37,7 +37,9 @@ KBUILD_CFLAGS := $(cflags-y) -Os -DDISABLE_BRANCH_PROFILING \
|
|||
KBUILD_CFLAGS := $(filter-out $(CC_FLAGS_SCS), $(KBUILD_CFLAGS))
|
||||
|
||||
GCOV_PROFILE := n
|
||||
# Sanitizer runtimes are unavailable and cannot be linked here.
|
||||
KASAN_SANITIZE := n
|
||||
KCSAN_SANITIZE := n
|
||||
UBSAN_SANITIZE := n
|
||||
OBJECT_FILES_NON_STANDARD := y
|
||||
|
||||
|
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
|
@ -11,7 +11,7 @@
|
|||
#ifndef _ASM_GENERIC_BITOPS_INSTRUMENTED_ATOMIC_H
|
||||
#define _ASM_GENERIC_BITOPS_INSTRUMENTED_ATOMIC_H
|
||||
|
||||
#include <linux/kasan-checks.h>
|
||||
#include <linux/instrumented.h>
|
||||
|
||||
/**
|
||||
* set_bit - Atomically set a bit in memory
|
||||
|
@ -25,7 +25,7 @@
|
|||
*/
|
||||
static inline void set_bit(long nr, volatile unsigned long *addr)
|
||||
{
|
||||
kasan_check_write(addr + BIT_WORD(nr), sizeof(long));
|
||||
instrument_atomic_write(addr + BIT_WORD(nr), sizeof(long));
|
||||
arch_set_bit(nr, addr);
|
||||
}
|
||||
|
||||
|
@ -38,7 +38,7 @@ static inline void set_bit(long nr, volatile unsigned long *addr)
|
|||
*/
|
||||
static inline void clear_bit(long nr, volatile unsigned long *addr)
|
||||
{
|
||||
kasan_check_write(addr + BIT_WORD(nr), sizeof(long));
|
||||
instrument_atomic_write(addr + BIT_WORD(nr), sizeof(long));
|
||||
arch_clear_bit(nr, addr);
|
||||
}
|
||||
|
||||
|
@ -54,7 +54,7 @@ static inline void clear_bit(long nr, volatile unsigned long *addr)
|
|||
*/
|
||||
static inline void change_bit(long nr, volatile unsigned long *addr)
|
||||
{
|
||||
kasan_check_write(addr + BIT_WORD(nr), sizeof(long));
|
||||
instrument_atomic_write(addr + BIT_WORD(nr), sizeof(long));
|
||||
arch_change_bit(nr, addr);
|
||||
}
|
||||
|
||||
|
@ -67,7 +67,7 @@ static inline void change_bit(long nr, volatile unsigned long *addr)
|
|||
*/
|
||||
static inline bool test_and_set_bit(long nr, volatile unsigned long *addr)
|
||||
{
|
||||
kasan_check_write(addr + BIT_WORD(nr), sizeof(long));
|
||||
instrument_atomic_write(addr + BIT_WORD(nr), sizeof(long));
|
||||
return arch_test_and_set_bit(nr, addr);
|
||||
}
|
||||
|
||||
|
@ -80,7 +80,7 @@ static inline bool test_and_set_bit(long nr, volatile unsigned long *addr)
|
|||
*/
|
||||
static inline bool test_and_clear_bit(long nr, volatile unsigned long *addr)
|
||||
{
|
||||
kasan_check_write(addr + BIT_WORD(nr), sizeof(long));
|
||||
instrument_atomic_write(addr + BIT_WORD(nr), sizeof(long));
|
||||
return arch_test_and_clear_bit(nr, addr);
|
||||
}
|
||||
|
||||
|
@ -93,7 +93,7 @@ static inline bool test_and_clear_bit(long nr, volatile unsigned long *addr)
|
|||
*/
|
||||
static inline bool test_and_change_bit(long nr, volatile unsigned long *addr)
|
||||
{
|
||||
kasan_check_write(addr + BIT_WORD(nr), sizeof(long));
|
||||
instrument_atomic_write(addr + BIT_WORD(nr), sizeof(long));
|
||||
return arch_test_and_change_bit(nr, addr);
|
||||
}
|
||||
|
||||
|
|
|
@ -11,7 +11,7 @@
|
|||
#ifndef _ASM_GENERIC_BITOPS_INSTRUMENTED_LOCK_H
|
||||
#define _ASM_GENERIC_BITOPS_INSTRUMENTED_LOCK_H
|
||||
|
||||
#include <linux/kasan-checks.h>
|
||||
#include <linux/instrumented.h>
|
||||
|
||||
/**
|
||||
* clear_bit_unlock - Clear a bit in memory, for unlock
|
||||
|
@ -22,7 +22,7 @@
|
|||
*/
|
||||
static inline void clear_bit_unlock(long nr, volatile unsigned long *addr)
|
||||
{
|
||||
kasan_check_write(addr + BIT_WORD(nr), sizeof(long));
|
||||
instrument_atomic_write(addr + BIT_WORD(nr), sizeof(long));
|
||||
arch_clear_bit_unlock(nr, addr);
|
||||
}
|
||||
|
||||
|
@ -37,7 +37,7 @@ static inline void clear_bit_unlock(long nr, volatile unsigned long *addr)
|
|||
*/
|
||||
static inline void __clear_bit_unlock(long nr, volatile unsigned long *addr)
|
||||
{
|
||||
kasan_check_write(addr + BIT_WORD(nr), sizeof(long));
|
||||
instrument_write(addr + BIT_WORD(nr), sizeof(long));
|
||||
arch___clear_bit_unlock(nr, addr);
|
||||
}
|
||||
|
||||
|
@ -52,7 +52,7 @@ static inline void __clear_bit_unlock(long nr, volatile unsigned long *addr)
|
|||
*/
|
||||
static inline bool test_and_set_bit_lock(long nr, volatile unsigned long *addr)
|
||||
{
|
||||
kasan_check_write(addr + BIT_WORD(nr), sizeof(long));
|
||||
instrument_atomic_write(addr + BIT_WORD(nr), sizeof(long));
|
||||
return arch_test_and_set_bit_lock(nr, addr);
|
||||
}
|
||||
|
||||
|
@ -71,7 +71,7 @@ static inline bool test_and_set_bit_lock(long nr, volatile unsigned long *addr)
|
|||
static inline bool
|
||||
clear_bit_unlock_is_negative_byte(long nr, volatile unsigned long *addr)
|
||||
{
|
||||
kasan_check_write(addr + BIT_WORD(nr), sizeof(long));
|
||||
instrument_atomic_write(addr + BIT_WORD(nr), sizeof(long));
|
||||
return arch_clear_bit_unlock_is_negative_byte(nr, addr);
|
||||
}
|
||||
/* Let everybody know we have it. */
|
||||
|
|
|
@ -11,7 +11,7 @@
|
|||
#ifndef _ASM_GENERIC_BITOPS_INSTRUMENTED_NON_ATOMIC_H
|
||||
#define _ASM_GENERIC_BITOPS_INSTRUMENTED_NON_ATOMIC_H
|
||||
|
||||
#include <linux/kasan-checks.h>
|
||||
#include <linux/instrumented.h>
|
||||
|
||||
/**
|
||||
* __set_bit - Set a bit in memory
|
||||
|
@ -24,7 +24,7 @@
|
|||
*/
|
||||
static inline void __set_bit(long nr, volatile unsigned long *addr)
|
||||
{
|
||||
kasan_check_write(addr + BIT_WORD(nr), sizeof(long));
|
||||
instrument_write(addr + BIT_WORD(nr), sizeof(long));
|
||||
arch___set_bit(nr, addr);
|
||||
}
|
||||
|
||||
|
@ -39,7 +39,7 @@ static inline void __set_bit(long nr, volatile unsigned long *addr)
|
|||
*/
|
||||
static inline void __clear_bit(long nr, volatile unsigned long *addr)
|
||||
{
|
||||
kasan_check_write(addr + BIT_WORD(nr), sizeof(long));
|
||||
instrument_write(addr + BIT_WORD(nr), sizeof(long));
|
||||
arch___clear_bit(nr, addr);
|
||||
}
|
||||
|
||||
|
@ -54,7 +54,7 @@ static inline void __clear_bit(long nr, volatile unsigned long *addr)
|
|||
*/
|
||||
static inline void __change_bit(long nr, volatile unsigned long *addr)
|
||||
{
|
||||
kasan_check_write(addr + BIT_WORD(nr), sizeof(long));
|
||||
instrument_write(addr + BIT_WORD(nr), sizeof(long));
|
||||
arch___change_bit(nr, addr);
|
||||
}
|
||||
|
||||
|
@ -68,7 +68,7 @@ static inline void __change_bit(long nr, volatile unsigned long *addr)
|
|||
*/
|
||||
static inline bool __test_and_set_bit(long nr, volatile unsigned long *addr)
|
||||
{
|
||||
kasan_check_write(addr + BIT_WORD(nr), sizeof(long));
|
||||
instrument_write(addr + BIT_WORD(nr), sizeof(long));
|
||||
return arch___test_and_set_bit(nr, addr);
|
||||
}
|
||||
|
||||
|
@ -82,7 +82,7 @@ static inline bool __test_and_set_bit(long nr, volatile unsigned long *addr)
|
|||
*/
|
||||
static inline bool __test_and_clear_bit(long nr, volatile unsigned long *addr)
|
||||
{
|
||||
kasan_check_write(addr + BIT_WORD(nr), sizeof(long));
|
||||
instrument_write(addr + BIT_WORD(nr), sizeof(long));
|
||||
return arch___test_and_clear_bit(nr, addr);
|
||||
}
|
||||
|
||||
|
@ -96,7 +96,7 @@ static inline bool __test_and_clear_bit(long nr, volatile unsigned long *addr)
|
|||
*/
|
||||
static inline bool __test_and_change_bit(long nr, volatile unsigned long *addr)
|
||||
{
|
||||
kasan_check_write(addr + BIT_WORD(nr), sizeof(long));
|
||||
instrument_write(addr + BIT_WORD(nr), sizeof(long));
|
||||
return arch___test_and_change_bit(nr, addr);
|
||||
}
|
||||
|
||||
|
@ -107,7 +107,7 @@ static inline bool __test_and_change_bit(long nr, volatile unsigned long *addr)
|
|||
*/
|
||||
static inline bool test_bit(long nr, const volatile unsigned long *addr)
|
||||
{
|
||||
kasan_check_read(addr + BIT_WORD(nr), sizeof(long));
|
||||
instrument_atomic_read(addr + BIT_WORD(nr), sizeof(long));
|
||||
return arch_test_bit(nr, addr);
|
||||
}
|
||||
|
||||
|
|
|
@ -16,7 +16,7 @@
|
|||
#define KASAN_ABI_VERSION 5
|
||||
|
||||
#if __has_feature(address_sanitizer) || __has_feature(hwaddress_sanitizer)
|
||||
/* emulate gcc's __SANITIZE_ADDRESS__ flag */
|
||||
/* Emulate GCC's __SANITIZE_ADDRESS__ flag */
|
||||
#define __SANITIZE_ADDRESS__
|
||||
#define __no_sanitize_address \
|
||||
__attribute__((no_sanitize("address", "hwaddress")))
|
||||
|
@ -24,6 +24,15 @@
|
|||
#define __no_sanitize_address
|
||||
#endif
|
||||
|
||||
#if __has_feature(thread_sanitizer)
|
||||
/* emulate gcc's __SANITIZE_THREAD__ flag */
|
||||
#define __SANITIZE_THREAD__
|
||||
#define __no_sanitize_thread \
|
||||
__attribute__((no_sanitize("thread")))
|
||||
#else
|
||||
#define __no_sanitize_thread
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Not all versions of clang implement the the type-generic versions
|
||||
* of the builtin overflow checkers. Fortunately, clang implements
|
||||
|
|
|
@ -144,6 +144,12 @@
|
|||
#define __no_sanitize_address
|
||||
#endif
|
||||
|
||||
#if defined(__SANITIZE_THREAD__) && __has_attribute(__no_sanitize_thread__)
|
||||
#define __no_sanitize_thread __attribute__((no_sanitize_thread))
|
||||
#else
|
||||
#define __no_sanitize_thread
|
||||
#endif
|
||||
|
||||
#if GCC_VERSION >= 50100
|
||||
#define COMPILER_HAS_GENERIC_BUILTIN_OVERFLOW 1
|
||||
#endif
|
||||
|
|
|
@ -250,6 +250,27 @@ void ftrace_likely_update(struct ftrace_likely_data *f, int val,
|
|||
*/
|
||||
#include <asm/barrier.h>
|
||||
#include <linux/kasan-checks.h>
|
||||
#include <linux/kcsan-checks.h>
|
||||
|
||||
/**
|
||||
* data_race - mark an expression as containing intentional data races
|
||||
*
|
||||
* This data_race() macro is useful for situations in which data races
|
||||
* should be forgiven. One example is diagnostic code that accesses
|
||||
* shared variables but is not a part of the core synchronization design.
|
||||
*
|
||||
* This macro *does not* affect normal code generation, but is a hint
|
||||
* to tooling that data races here are to be ignored.
|
||||
*/
|
||||
#define data_race(expr) \
|
||||
({ \
|
||||
__unqual_scalar_typeof(({ expr; })) __v = ({ \
|
||||
__kcsan_disable_current(); \
|
||||
expr; \
|
||||
}); \
|
||||
__kcsan_enable_current(); \
|
||||
__v; \
|
||||
})
|
||||
|
||||
/*
|
||||
* Use __READ_ONCE() instead of READ_ONCE() if you do not require any
|
||||
|
@ -271,30 +292,18 @@ void ftrace_likely_update(struct ftrace_likely_data *f, int val,
|
|||
__READ_ONCE_SCALAR(x); \
|
||||
})
|
||||
|
||||
#define __WRITE_ONCE(x, val) \
|
||||
do { \
|
||||
*(volatile typeof(x) *)&(x) = (val); \
|
||||
#define __WRITE_ONCE(x, val) \
|
||||
do { \
|
||||
*(volatile typeof(x) *)&(x) = (val); \
|
||||
} while (0)
|
||||
|
||||
#define WRITE_ONCE(x, val) \
|
||||
do { \
|
||||
compiletime_assert_rwonce_type(x); \
|
||||
__WRITE_ONCE(x, val); \
|
||||
#define WRITE_ONCE(x, val) \
|
||||
do { \
|
||||
compiletime_assert_rwonce_type(x); \
|
||||
__WRITE_ONCE(x, val); \
|
||||
} while (0)
|
||||
|
||||
#ifdef CONFIG_KASAN
|
||||
/*
|
||||
* We can't declare function 'inline' because __no_sanitize_address conflicts
|
||||
* with inlining. Attempt to inline it may cause a build failure.
|
||||
* https://gcc.gnu.org/bugzilla/show_bug.cgi?id=67368
|
||||
* '__maybe_unused' allows us to avoid defined-but-not-used warnings.
|
||||
*/
|
||||
# define __no_kasan_or_inline __no_sanitize_address notrace __maybe_unused
|
||||
#else
|
||||
# define __no_kasan_or_inline __always_inline
|
||||
#endif
|
||||
|
||||
static __no_kasan_or_inline
|
||||
static __no_sanitize_or_inline
|
||||
unsigned long __read_once_word_nocheck(const void *addr)
|
||||
{
|
||||
return __READ_ONCE(*(unsigned long *)addr);
|
||||
|
@ -302,8 +311,8 @@ unsigned long __read_once_word_nocheck(const void *addr)
|
|||
|
||||
/*
|
||||
* Use READ_ONCE_NOCHECK() instead of READ_ONCE() if you need to load a
|
||||
* word from memory atomically but without telling KASAN. This is usually
|
||||
* used by unwinding code when walking the stack of a running process.
|
||||
* word from memory atomically but without telling KASAN/KCSAN. This is
|
||||
* usually used by unwinding code when walking the stack of a running process.
|
||||
*/
|
||||
#define READ_ONCE_NOCHECK(x) \
|
||||
({ \
|
||||
|
|
|
@ -171,6 +171,38 @@ struct ftrace_likely_data {
|
|||
*/
|
||||
#define noinline_for_stack noinline
|
||||
|
||||
/*
|
||||
* Sanitizer helper attributes: Because using __always_inline and
|
||||
* __no_sanitize_* conflict, provide helper attributes that will either expand
|
||||
* to __no_sanitize_* in compilation units where instrumentation is enabled
|
||||
* (__SANITIZE_*__), or __always_inline in compilation units without
|
||||
* instrumentation (__SANITIZE_*__ undefined).
|
||||
*/
|
||||
#ifdef __SANITIZE_ADDRESS__
|
||||
/*
|
||||
* We can't declare function 'inline' because __no_sanitize_address conflicts
|
||||
* with inlining. Attempt to inline it may cause a build failure.
|
||||
* https://gcc.gnu.org/bugzilla/show_bug.cgi?id=67368
|
||||
* '__maybe_unused' allows us to avoid defined-but-not-used warnings.
|
||||
*/
|
||||
# define __no_kasan_or_inline __no_sanitize_address notrace __maybe_unused
|
||||
# define __no_sanitize_or_inline __no_kasan_or_inline
|
||||
#else
|
||||
# define __no_kasan_or_inline __always_inline
|
||||
#endif
|
||||
|
||||
#define __no_kcsan __no_sanitize_thread
|
||||
#ifdef __SANITIZE_THREAD__
|
||||
# define __no_kcsan_or_inline __no_kcsan notrace __maybe_unused
|
||||
# define __no_sanitize_or_inline __no_kcsan_or_inline
|
||||
#else
|
||||
# define __no_kcsan_or_inline __always_inline
|
||||
#endif
|
||||
|
||||
#ifndef __no_sanitize_or_inline
|
||||
#define __no_sanitize_or_inline __always_inline
|
||||
#endif
|
||||
|
||||
#endif /* __KERNEL__ */
|
||||
|
||||
#endif /* __ASSEMBLY__ */
|
||||
|
|
|
@ -0,0 +1,109 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
|
||||
/*
|
||||
* This header provides generic wrappers for memory access instrumentation that
|
||||
* the compiler cannot emit for: KASAN, KCSAN.
|
||||
*/
|
||||
#ifndef _LINUX_INSTRUMENTED_H
|
||||
#define _LINUX_INSTRUMENTED_H
|
||||
|
||||
#include <linux/compiler.h>
|
||||
#include <linux/kasan-checks.h>
|
||||
#include <linux/kcsan-checks.h>
|
||||
#include <linux/types.h>
|
||||
|
||||
/**
|
||||
* instrument_read - instrument regular read access
|
||||
*
|
||||
* Instrument a regular read access. The instrumentation should be inserted
|
||||
* before the actual read happens.
|
||||
*
|
||||
* @ptr address of access
|
||||
* @size size of access
|
||||
*/
|
||||
static __always_inline void instrument_read(const volatile void *v, size_t size)
|
||||
{
|
||||
kasan_check_read(v, size);
|
||||
kcsan_check_read(v, size);
|
||||
}
|
||||
|
||||
/**
|
||||
* instrument_write - instrument regular write access
|
||||
*
|
||||
* Instrument a regular write access. The instrumentation should be inserted
|
||||
* before the actual write happens.
|
||||
*
|
||||
* @ptr address of access
|
||||
* @size size of access
|
||||
*/
|
||||
static __always_inline void instrument_write(const volatile void *v, size_t size)
|
||||
{
|
||||
kasan_check_write(v, size);
|
||||
kcsan_check_write(v, size);
|
||||
}
|
||||
|
||||
/**
|
||||
* instrument_atomic_read - instrument atomic read access
|
||||
*
|
||||
* Instrument an atomic read access. The instrumentation should be inserted
|
||||
* before the actual read happens.
|
||||
*
|
||||
* @ptr address of access
|
||||
* @size size of access
|
||||
*/
|
||||
static __always_inline void instrument_atomic_read(const volatile void *v, size_t size)
|
||||
{
|
||||
kasan_check_read(v, size);
|
||||
kcsan_check_atomic_read(v, size);
|
||||
}
|
||||
|
||||
/**
|
||||
* instrument_atomic_write - instrument atomic write access
|
||||
*
|
||||
* Instrument an atomic write access. The instrumentation should be inserted
|
||||
* before the actual write happens.
|
||||
*
|
||||
* @ptr address of access
|
||||
* @size size of access
|
||||
*/
|
||||
static __always_inline void instrument_atomic_write(const volatile void *v, size_t size)
|
||||
{
|
||||
kasan_check_write(v, size);
|
||||
kcsan_check_atomic_write(v, size);
|
||||
}
|
||||
|
||||
/**
|
||||
* instrument_copy_to_user - instrument reads of copy_to_user
|
||||
*
|
||||
* Instrument reads from kernel memory, that are due to copy_to_user (and
|
||||
* variants). The instrumentation must be inserted before the accesses.
|
||||
*
|
||||
* @to destination address
|
||||
* @from source address
|
||||
* @n number of bytes to copy
|
||||
*/
|
||||
static __always_inline void
|
||||
instrument_copy_to_user(void __user *to, const void *from, unsigned long n)
|
||||
{
|
||||
kasan_check_read(from, n);
|
||||
kcsan_check_read(from, n);
|
||||
}
|
||||
|
||||
/**
|
||||
* instrument_copy_from_user - instrument writes of copy_from_user
|
||||
*
|
||||
* Instrument writes to kernel memory, that are due to copy_from_user (and
|
||||
* variants). The instrumentation should be inserted before the accesses.
|
||||
*
|
||||
* @to destination address
|
||||
* @from source address
|
||||
* @n number of bytes to copy
|
||||
*/
|
||||
static __always_inline void
|
||||
instrument_copy_from_user(const void *to, const void __user *from, unsigned long n)
|
||||
{
|
||||
kasan_check_write(to, n);
|
||||
kcsan_check_write(to, n);
|
||||
}
|
||||
|
||||
#endif /* _LINUX_INSTRUMENTED_H */
|
|
@ -0,0 +1,430 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
|
||||
#ifndef _LINUX_KCSAN_CHECKS_H
|
||||
#define _LINUX_KCSAN_CHECKS_H
|
||||
|
||||
/* Note: Only include what is already included by compiler.h. */
|
||||
#include <linux/compiler_attributes.h>
|
||||
#include <linux/types.h>
|
||||
|
||||
/*
|
||||
* ACCESS TYPE MODIFIERS
|
||||
*
|
||||
* <none>: normal read access;
|
||||
* WRITE : write access;
|
||||
* ATOMIC: access is atomic;
|
||||
* ASSERT: access is not a regular access, but an assertion;
|
||||
* SCOPED: access is a scoped access;
|
||||
*/
|
||||
#define KCSAN_ACCESS_WRITE 0x1
|
||||
#define KCSAN_ACCESS_ATOMIC 0x2
|
||||
#define KCSAN_ACCESS_ASSERT 0x4
|
||||
#define KCSAN_ACCESS_SCOPED 0x8
|
||||
|
||||
/*
|
||||
* __kcsan_*: Always calls into the runtime when KCSAN is enabled. This may be used
|
||||
* even in compilation units that selectively disable KCSAN, but must use KCSAN
|
||||
* to validate access to an address. Never use these in header files!
|
||||
*/
|
||||
#ifdef CONFIG_KCSAN
|
||||
/**
|
||||
* __kcsan_check_access - check generic access for races
|
||||
*
|
||||
* @ptr: address of access
|
||||
* @size: size of access
|
||||
* @type: access type modifier
|
||||
*/
|
||||
void __kcsan_check_access(const volatile void *ptr, size_t size, int type);
|
||||
|
||||
/**
|
||||
* kcsan_disable_current - disable KCSAN for the current context
|
||||
*
|
||||
* Supports nesting.
|
||||
*/
|
||||
void kcsan_disable_current(void);
|
||||
|
||||
/**
|
||||
* kcsan_enable_current - re-enable KCSAN for the current context
|
||||
*
|
||||
* Supports nesting.
|
||||
*/
|
||||
void kcsan_enable_current(void);
|
||||
void kcsan_enable_current_nowarn(void); /* Safe in uaccess regions. */
|
||||
|
||||
/**
|
||||
* kcsan_nestable_atomic_begin - begin nestable atomic region
|
||||
*
|
||||
* Accesses within the atomic region may appear to race with other accesses but
|
||||
* should be considered atomic.
|
||||
*/
|
||||
void kcsan_nestable_atomic_begin(void);
|
||||
|
||||
/**
|
||||
* kcsan_nestable_atomic_end - end nestable atomic region
|
||||
*/
|
||||
void kcsan_nestable_atomic_end(void);
|
||||
|
||||
/**
|
||||
* kcsan_flat_atomic_begin - begin flat atomic region
|
||||
*
|
||||
* Accesses within the atomic region may appear to race with other accesses but
|
||||
* should be considered atomic.
|
||||
*/
|
||||
void kcsan_flat_atomic_begin(void);
|
||||
|
||||
/**
|
||||
* kcsan_flat_atomic_end - end flat atomic region
|
||||
*/
|
||||
void kcsan_flat_atomic_end(void);
|
||||
|
||||
/**
|
||||
* kcsan_atomic_next - consider following accesses as atomic
|
||||
*
|
||||
* Force treating the next n memory accesses for the current context as atomic
|
||||
* operations.
|
||||
*
|
||||
* @n: number of following memory accesses to treat as atomic.
|
||||
*/
|
||||
void kcsan_atomic_next(int n);
|
||||
|
||||
/**
|
||||
* kcsan_set_access_mask - set access mask
|
||||
*
|
||||
* Set the access mask for all accesses for the current context if non-zero.
|
||||
* Only value changes to bits set in the mask will be reported.
|
||||
*
|
||||
* @mask: bitmask
|
||||
*/
|
||||
void kcsan_set_access_mask(unsigned long mask);
|
||||
|
||||
/* Scoped access information. */
|
||||
struct kcsan_scoped_access {
|
||||
struct list_head list;
|
||||
const volatile void *ptr;
|
||||
size_t size;
|
||||
int type;
|
||||
};
|
||||
/*
|
||||
* Automatically call kcsan_end_scoped_access() when kcsan_scoped_access goes
|
||||
* out of scope; relies on attribute "cleanup", which is supported by all
|
||||
* compilers that support KCSAN.
|
||||
*/
|
||||
#define __kcsan_cleanup_scoped \
|
||||
__maybe_unused __attribute__((__cleanup__(kcsan_end_scoped_access)))
|
||||
|
||||
/**
|
||||
* kcsan_begin_scoped_access - begin scoped access
|
||||
*
|
||||
* Begin scoped access and initialize @sa, which will cause KCSAN to
|
||||
* continuously check the memory range in the current thread until
|
||||
* kcsan_end_scoped_access() is called for @sa.
|
||||
*
|
||||
* Scoped accesses are implemented by appending @sa to an internal list for the
|
||||
* current execution context, and then checked on every call into the KCSAN
|
||||
* runtime.
|
||||
*
|
||||
* @ptr: address of access
|
||||
* @size: size of access
|
||||
* @type: access type modifier
|
||||
* @sa: struct kcsan_scoped_access to use for the scope of the access
|
||||
*/
|
||||
struct kcsan_scoped_access *
|
||||
kcsan_begin_scoped_access(const volatile void *ptr, size_t size, int type,
|
||||
struct kcsan_scoped_access *sa);
|
||||
|
||||
/**
|
||||
* kcsan_end_scoped_access - end scoped access
|
||||
*
|
||||
* End a scoped access, which will stop KCSAN checking the memory range.
|
||||
* Requires that kcsan_begin_scoped_access() was previously called once for @sa.
|
||||
*
|
||||
* @sa: a previously initialized struct kcsan_scoped_access
|
||||
*/
|
||||
void kcsan_end_scoped_access(struct kcsan_scoped_access *sa);
|
||||
|
||||
|
||||
#else /* CONFIG_KCSAN */
|
||||
|
||||
static inline void __kcsan_check_access(const volatile void *ptr, size_t size,
|
||||
int type) { }
|
||||
|
||||
static inline void kcsan_disable_current(void) { }
|
||||
static inline void kcsan_enable_current(void) { }
|
||||
static inline void kcsan_enable_current_nowarn(void) { }
|
||||
static inline void kcsan_nestable_atomic_begin(void) { }
|
||||
static inline void kcsan_nestable_atomic_end(void) { }
|
||||
static inline void kcsan_flat_atomic_begin(void) { }
|
||||
static inline void kcsan_flat_atomic_end(void) { }
|
||||
static inline void kcsan_atomic_next(int n) { }
|
||||
static inline void kcsan_set_access_mask(unsigned long mask) { }
|
||||
|
||||
struct kcsan_scoped_access { };
|
||||
#define __kcsan_cleanup_scoped __maybe_unused
|
||||
static inline struct kcsan_scoped_access *
|
||||
kcsan_begin_scoped_access(const volatile void *ptr, size_t size, int type,
|
||||
struct kcsan_scoped_access *sa) { return sa; }
|
||||
static inline void kcsan_end_scoped_access(struct kcsan_scoped_access *sa) { }
|
||||
|
||||
#endif /* CONFIG_KCSAN */
|
||||
|
||||
#ifdef __SANITIZE_THREAD__
|
||||
/*
|
||||
* Only calls into the runtime when the particular compilation unit has KCSAN
|
||||
* instrumentation enabled. May be used in header files.
|
||||
*/
|
||||
#define kcsan_check_access __kcsan_check_access
|
||||
|
||||
/*
|
||||
* Only use these to disable KCSAN for accesses in the current compilation unit;
|
||||
* calls into libraries may still perform KCSAN checks.
|
||||
*/
|
||||
#define __kcsan_disable_current kcsan_disable_current
|
||||
#define __kcsan_enable_current kcsan_enable_current_nowarn
|
||||
#else
|
||||
static inline void kcsan_check_access(const volatile void *ptr, size_t size,
|
||||
int type) { }
|
||||
static inline void __kcsan_enable_current(void) { }
|
||||
static inline void __kcsan_disable_current(void) { }
|
||||
#endif
|
||||
|
||||
/**
|
||||
* __kcsan_check_read - check regular read access for races
|
||||
*
|
||||
* @ptr: address of access
|
||||
* @size: size of access
|
||||
*/
|
||||
#define __kcsan_check_read(ptr, size) __kcsan_check_access(ptr, size, 0)
|
||||
|
||||
/**
|
||||
* __kcsan_check_write - check regular write access for races
|
||||
*
|
||||
* @ptr: address of access
|
||||
* @size: size of access
|
||||
*/
|
||||
#define __kcsan_check_write(ptr, size) \
|
||||
__kcsan_check_access(ptr, size, KCSAN_ACCESS_WRITE)
|
||||
|
||||
/**
|
||||
* kcsan_check_read - check regular read access for races
|
||||
*
|
||||
* @ptr: address of access
|
||||
* @size: size of access
|
||||
*/
|
||||
#define kcsan_check_read(ptr, size) kcsan_check_access(ptr, size, 0)
|
||||
|
||||
/**
|
||||
* kcsan_check_write - check regular write access for races
|
||||
*
|
||||
* @ptr: address of access
|
||||
* @size: size of access
|
||||
*/
|
||||
#define kcsan_check_write(ptr, size) \
|
||||
kcsan_check_access(ptr, size, KCSAN_ACCESS_WRITE)
|
||||
|
||||
/*
|
||||
* Check for atomic accesses: if atomic accesses are not ignored, this simply
|
||||
* aliases to kcsan_check_access(), otherwise becomes a no-op.
|
||||
*/
|
||||
#ifdef CONFIG_KCSAN_IGNORE_ATOMICS
|
||||
#define kcsan_check_atomic_read(...) do { } while (0)
|
||||
#define kcsan_check_atomic_write(...) do { } while (0)
|
||||
#else
|
||||
#define kcsan_check_atomic_read(ptr, size) \
|
||||
kcsan_check_access(ptr, size, KCSAN_ACCESS_ATOMIC)
|
||||
#define kcsan_check_atomic_write(ptr, size) \
|
||||
kcsan_check_access(ptr, size, KCSAN_ACCESS_ATOMIC | KCSAN_ACCESS_WRITE)
|
||||
#endif
|
||||
|
||||
/**
|
||||
* ASSERT_EXCLUSIVE_WRITER - assert no concurrent writes to @var
|
||||
*
|
||||
* Assert that there are no concurrent writes to @var; other readers are
|
||||
* allowed. This assertion can be used to specify properties of concurrent code,
|
||||
* where violation cannot be detected as a normal data race.
|
||||
*
|
||||
* For example, if we only have a single writer, but multiple concurrent
|
||||
* readers, to avoid data races, all these accesses must be marked; even
|
||||
* concurrent marked writes racing with the single writer are bugs.
|
||||
* Unfortunately, due to being marked, they are no longer data races. For cases
|
||||
* like these, we can use the macro as follows:
|
||||
*
|
||||
* .. code-block:: c
|
||||
*
|
||||
* void writer(void) {
|
||||
* spin_lock(&update_foo_lock);
|
||||
* ASSERT_EXCLUSIVE_WRITER(shared_foo);
|
||||
* WRITE_ONCE(shared_foo, ...);
|
||||
* spin_unlock(&update_foo_lock);
|
||||
* }
|
||||
* void reader(void) {
|
||||
* // update_foo_lock does not need to be held!
|
||||
* ... = READ_ONCE(shared_foo);
|
||||
* }
|
||||
*
|
||||
* Note: ASSERT_EXCLUSIVE_WRITER_SCOPED(), if applicable, performs more thorough
|
||||
* checking if a clear scope where no concurrent writes are expected exists.
|
||||
*
|
||||
* @var: variable to assert on
|
||||
*/
|
||||
#define ASSERT_EXCLUSIVE_WRITER(var) \
|
||||
__kcsan_check_access(&(var), sizeof(var), KCSAN_ACCESS_ASSERT)
|
||||
|
||||
/*
|
||||
* Helper macros for implementation of for ASSERT_EXCLUSIVE_*_SCOPED(). @id is
|
||||
* expected to be unique for the scope in which instances of kcsan_scoped_access
|
||||
* are declared.
|
||||
*/
|
||||
#define __kcsan_scoped_name(c, suffix) __kcsan_scoped_##c##suffix
|
||||
#define __ASSERT_EXCLUSIVE_SCOPED(var, type, id) \
|
||||
struct kcsan_scoped_access __kcsan_scoped_name(id, _) \
|
||||
__kcsan_cleanup_scoped; \
|
||||
struct kcsan_scoped_access *__kcsan_scoped_name(id, _dummy_p) \
|
||||
__maybe_unused = kcsan_begin_scoped_access( \
|
||||
&(var), sizeof(var), KCSAN_ACCESS_SCOPED | (type), \
|
||||
&__kcsan_scoped_name(id, _))
|
||||
|
||||
/**
|
||||
* ASSERT_EXCLUSIVE_WRITER_SCOPED - assert no concurrent writes to @var in scope
|
||||
*
|
||||
* Scoped variant of ASSERT_EXCLUSIVE_WRITER().
|
||||
*
|
||||
* Assert that there are no concurrent writes to @var for the duration of the
|
||||
* scope in which it is introduced. This provides a better way to fully cover
|
||||
* the enclosing scope, compared to multiple ASSERT_EXCLUSIVE_WRITER(), and
|
||||
* increases the likelihood for KCSAN to detect racing accesses.
|
||||
*
|
||||
* For example, it allows finding race-condition bugs that only occur due to
|
||||
* state changes within the scope itself:
|
||||
*
|
||||
* .. code-block:: c
|
||||
*
|
||||
* void writer(void) {
|
||||
* spin_lock(&update_foo_lock);
|
||||
* {
|
||||
* ASSERT_EXCLUSIVE_WRITER_SCOPED(shared_foo);
|
||||
* WRITE_ONCE(shared_foo, 42);
|
||||
* ...
|
||||
* // shared_foo should still be 42 here!
|
||||
* }
|
||||
* spin_unlock(&update_foo_lock);
|
||||
* }
|
||||
* void buggy(void) {
|
||||
* if (READ_ONCE(shared_foo) == 42)
|
||||
* WRITE_ONCE(shared_foo, 1); // bug!
|
||||
* }
|
||||
*
|
||||
* @var: variable to assert on
|
||||
*/
|
||||
#define ASSERT_EXCLUSIVE_WRITER_SCOPED(var) \
|
||||
__ASSERT_EXCLUSIVE_SCOPED(var, KCSAN_ACCESS_ASSERT, __COUNTER__)
|
||||
|
||||
/**
|
||||
* ASSERT_EXCLUSIVE_ACCESS - assert no concurrent accesses to @var
|
||||
*
|
||||
* Assert that there are no concurrent accesses to @var (no readers nor
|
||||
* writers). This assertion can be used to specify properties of concurrent
|
||||
* code, where violation cannot be detected as a normal data race.
|
||||
*
|
||||
* For example, where exclusive access is expected after determining no other
|
||||
* users of an object are left, but the object is not actually freed. We can
|
||||
* check that this property actually holds as follows:
|
||||
*
|
||||
* .. code-block:: c
|
||||
*
|
||||
* if (refcount_dec_and_test(&obj->refcnt)) {
|
||||
* ASSERT_EXCLUSIVE_ACCESS(*obj);
|
||||
* do_some_cleanup(obj);
|
||||
* release_for_reuse(obj);
|
||||
* }
|
||||
*
|
||||
* Note: ASSERT_EXCLUSIVE_ACCESS_SCOPED(), if applicable, performs more thorough
|
||||
* checking if a clear scope where no concurrent accesses are expected exists.
|
||||
*
|
||||
* Note: For cases where the object is freed, `KASAN <kasan.html>`_ is a better
|
||||
* fit to detect use-after-free bugs.
|
||||
*
|
||||
* @var: variable to assert on
|
||||
*/
|
||||
#define ASSERT_EXCLUSIVE_ACCESS(var) \
|
||||
__kcsan_check_access(&(var), sizeof(var), KCSAN_ACCESS_WRITE | KCSAN_ACCESS_ASSERT)
|
||||
|
||||
/**
|
||||
* ASSERT_EXCLUSIVE_ACCESS_SCOPED - assert no concurrent accesses to @var in scope
|
||||
*
|
||||
* Scoped variant of ASSERT_EXCLUSIVE_ACCESS().
|
||||
*
|
||||
* Assert that there are no concurrent accesses to @var (no readers nor writers)
|
||||
* for the entire duration of the scope in which it is introduced. This provides
|
||||
* a better way to fully cover the enclosing scope, compared to multiple
|
||||
* ASSERT_EXCLUSIVE_ACCESS(), and increases the likelihood for KCSAN to detect
|
||||
* racing accesses.
|
||||
*
|
||||
* @var: variable to assert on
|
||||
*/
|
||||
#define ASSERT_EXCLUSIVE_ACCESS_SCOPED(var) \
|
||||
__ASSERT_EXCLUSIVE_SCOPED(var, KCSAN_ACCESS_WRITE | KCSAN_ACCESS_ASSERT, __COUNTER__)
|
||||
|
||||
/**
|
||||
* ASSERT_EXCLUSIVE_BITS - assert no concurrent writes to subset of bits in @var
|
||||
*
|
||||
* Bit-granular variant of ASSERT_EXCLUSIVE_WRITER().
|
||||
*
|
||||
* Assert that there are no concurrent writes to a subset of bits in @var;
|
||||
* concurrent readers are permitted. This assertion captures more detailed
|
||||
* bit-level properties, compared to the other (word granularity) assertions.
|
||||
* Only the bits set in @mask are checked for concurrent modifications, while
|
||||
* ignoring the remaining bits, i.e. concurrent writes (or reads) to ~mask bits
|
||||
* are ignored.
|
||||
*
|
||||
* Use this for variables, where some bits must not be modified concurrently,
|
||||
* yet other bits are expected to be modified concurrently.
|
||||
*
|
||||
* For example, variables where, after initialization, some bits are read-only,
|
||||
* but other bits may still be modified concurrently. A reader may wish to
|
||||
* assert that this is true as follows:
|
||||
*
|
||||
* .. code-block:: c
|
||||
*
|
||||
* ASSERT_EXCLUSIVE_BITS(flags, READ_ONLY_MASK);
|
||||
* foo = (READ_ONCE(flags) & READ_ONLY_MASK) >> READ_ONLY_SHIFT;
|
||||
*
|
||||
* Note: The access that immediately follows ASSERT_EXCLUSIVE_BITS() is assumed
|
||||
* to access the masked bits only, and KCSAN optimistically assumes it is
|
||||
* therefore safe, even in the presence of data races, and marking it with
|
||||
* READ_ONCE() is optional from KCSAN's point-of-view. We caution, however, that
|
||||
* it may still be advisable to do so, since we cannot reason about all compiler
|
||||
* optimizations when it comes to bit manipulations (on the reader and writer
|
||||
* side). If you are sure nothing can go wrong, we can write the above simply
|
||||
* as:
|
||||
*
|
||||
* .. code-block:: c
|
||||
*
|
||||
* ASSERT_EXCLUSIVE_BITS(flags, READ_ONLY_MASK);
|
||||
* foo = (flags & READ_ONLY_MASK) >> READ_ONLY_SHIFT;
|
||||
*
|
||||
* Another example, where this may be used, is when certain bits of @var may
|
||||
* only be modified when holding the appropriate lock, but other bits may still
|
||||
* be modified concurrently. Writers, where other bits may change concurrently,
|
||||
* could use the assertion as follows:
|
||||
*
|
||||
* .. code-block:: c
|
||||
*
|
||||
* spin_lock(&foo_lock);
|
||||
* ASSERT_EXCLUSIVE_BITS(flags, FOO_MASK);
|
||||
* old_flags = flags;
|
||||
* new_flags = (old_flags & ~FOO_MASK) | (new_foo << FOO_SHIFT);
|
||||
* if (cmpxchg(&flags, old_flags, new_flags) != old_flags) { ... }
|
||||
* spin_unlock(&foo_lock);
|
||||
*
|
||||
* @var: variable to assert on
|
||||
* @mask: only check for modifications to bits set in @mask
|
||||
*/
|
||||
#define ASSERT_EXCLUSIVE_BITS(var, mask) \
|
||||
do { \
|
||||
kcsan_set_access_mask(mask); \
|
||||
__kcsan_check_access(&(var), sizeof(var), KCSAN_ACCESS_ASSERT);\
|
||||
kcsan_set_access_mask(0); \
|
||||
kcsan_atomic_next(1); \
|
||||
} while (0)
|
||||
|
||||
#endif /* _LINUX_KCSAN_CHECKS_H */
|
|
@ -0,0 +1,59 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
|
||||
#ifndef _LINUX_KCSAN_H
|
||||
#define _LINUX_KCSAN_H
|
||||
|
||||
#include <linux/kcsan-checks.h>
|
||||
#include <linux/types.h>
|
||||
|
||||
#ifdef CONFIG_KCSAN
|
||||
|
||||
/*
|
||||
* Context for each thread of execution: for tasks, this is stored in
|
||||
* task_struct, and interrupts access internal per-CPU storage.
|
||||
*/
|
||||
struct kcsan_ctx {
|
||||
int disable_count; /* disable counter */
|
||||
int atomic_next; /* number of following atomic ops */
|
||||
|
||||
/*
|
||||
* We distinguish between: (a) nestable atomic regions that may contain
|
||||
* other nestable regions; and (b) flat atomic regions that do not keep
|
||||
* track of nesting. Both (a) and (b) are entirely independent of each
|
||||
* other, and a flat region may be started in a nestable region or
|
||||
* vice-versa.
|
||||
*
|
||||
* This is required because, for example, in the annotations for
|
||||
* seqlocks, we declare seqlock writer critical sections as (a) nestable
|
||||
* atomic regions, but reader critical sections as (b) flat atomic
|
||||
* regions, but have encountered cases where seqlock reader critical
|
||||
* sections are contained within writer critical sections (the opposite
|
||||
* may be possible, too).
|
||||
*
|
||||
* To support these cases, we independently track the depth of nesting
|
||||
* for (a), and whether the leaf level is flat for (b).
|
||||
*/
|
||||
int atomic_nest_count;
|
||||
bool in_flat_atomic;
|
||||
|
||||
/*
|
||||
* Access mask for all accesses if non-zero.
|
||||
*/
|
||||
unsigned long access_mask;
|
||||
|
||||
/* List of scoped accesses. */
|
||||
struct list_head scoped_accesses;
|
||||
};
|
||||
|
||||
/**
|
||||
* kcsan_init - initialize KCSAN runtime
|
||||
*/
|
||||
void kcsan_init(void);
|
||||
|
||||
#else /* CONFIG_KCSAN */
|
||||
|
||||
static inline void kcsan_init(void) { }
|
||||
|
||||
#endif /* CONFIG_KCSAN */
|
||||
|
||||
#endif /* _LINUX_KCSAN_H */
|
|
@ -31,6 +31,7 @@
|
|||
#include <linux/task_io_accounting.h>
|
||||
#include <linux/posix-timers.h>
|
||||
#include <linux/rseq.h>
|
||||
#include <linux/kcsan.h>
|
||||
|
||||
/* task_struct member predeclarations (sorted alphabetically): */
|
||||
struct audit_context;
|
||||
|
@ -1197,6 +1198,9 @@ struct task_struct {
|
|||
#ifdef CONFIG_KASAN
|
||||
unsigned int kasan_depth;
|
||||
#endif
|
||||
#ifdef CONFIG_KCSAN
|
||||
struct kcsan_ctx kcsan_ctx;
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_FUNCTION_GRAPH_TRACER
|
||||
/* Index of current stored address in ret_stack: */
|
||||
|
|
|
@ -37,8 +37,24 @@
|
|||
#include <linux/preempt.h>
|
||||
#include <linux/lockdep.h>
|
||||
#include <linux/compiler.h>
|
||||
#include <linux/kcsan-checks.h>
|
||||
#include <asm/processor.h>
|
||||
|
||||
/*
|
||||
* The seqlock interface does not prescribe a precise sequence of read
|
||||
* begin/retry/end. For readers, typically there is a call to
|
||||
* read_seqcount_begin() and read_seqcount_retry(), however, there are more
|
||||
* esoteric cases which do not follow this pattern.
|
||||
*
|
||||
* As a consequence, we take the following best-effort approach for raw usage
|
||||
* via seqcount_t under KCSAN: upon beginning a seq-reader critical section,
|
||||
* pessimistically mark the next KCSAN_SEQLOCK_REGION_MAX memory accesses as
|
||||
* atomics; if there is a matching read_seqcount_retry() call, no following
|
||||
* memory operations are considered atomic. Usage of seqlocks via seqlock_t
|
||||
* interface is not affected.
|
||||
*/
|
||||
#define KCSAN_SEQLOCK_REGION_MAX 1000
|
||||
|
||||
/*
|
||||
* Version using sequence counter only.
|
||||
* This can be used when code has its own mutex protecting the
|
||||
|
@ -115,6 +131,7 @@ repeat:
|
|||
cpu_relax();
|
||||
goto repeat;
|
||||
}
|
||||
kcsan_atomic_next(KCSAN_SEQLOCK_REGION_MAX);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
@ -131,6 +148,7 @@ static inline unsigned raw_read_seqcount(const seqcount_t *s)
|
|||
{
|
||||
unsigned ret = READ_ONCE(s->sequence);
|
||||
smp_rmb();
|
||||
kcsan_atomic_next(KCSAN_SEQLOCK_REGION_MAX);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
@ -183,6 +201,7 @@ static inline unsigned raw_seqcount_begin(const seqcount_t *s)
|
|||
{
|
||||
unsigned ret = READ_ONCE(s->sequence);
|
||||
smp_rmb();
|
||||
kcsan_atomic_next(KCSAN_SEQLOCK_REGION_MAX);
|
||||
return ret & ~1;
|
||||
}
|
||||
|
||||
|
@ -202,7 +221,8 @@ static inline unsigned raw_seqcount_begin(const seqcount_t *s)
|
|||
*/
|
||||
static inline int __read_seqcount_retry(const seqcount_t *s, unsigned start)
|
||||
{
|
||||
return unlikely(s->sequence != start);
|
||||
kcsan_atomic_next(0);
|
||||
return unlikely(READ_ONCE(s->sequence) != start);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -225,6 +245,7 @@ static inline int read_seqcount_retry(const seqcount_t *s, unsigned start)
|
|||
|
||||
static inline void raw_write_seqcount_begin(seqcount_t *s)
|
||||
{
|
||||
kcsan_nestable_atomic_begin();
|
||||
s->sequence++;
|
||||
smp_wmb();
|
||||
}
|
||||
|
@ -233,6 +254,7 @@ static inline void raw_write_seqcount_end(seqcount_t *s)
|
|||
{
|
||||
smp_wmb();
|
||||
s->sequence++;
|
||||
kcsan_nestable_atomic_end();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -243,6 +265,13 @@ static inline void raw_write_seqcount_end(seqcount_t *s)
|
|||
* usual consistency guarantee. It is one wmb cheaper, because we can
|
||||
* collapse the two back-to-back wmb()s.
|
||||
*
|
||||
* Note that writes surrounding the barrier should be declared atomic (e.g.
|
||||
* via WRITE_ONCE): a) to ensure the writes become visible to other threads
|
||||
* atomically, avoiding compiler optimizations; b) to document which writes are
|
||||
* meant to propagate to the reader critical section. This is necessary because
|
||||
* neither writes before and after the barrier are enclosed in a seq-writer
|
||||
* critical section that would ensure readers are aware of ongoing writes.
|
||||
*
|
||||
* seqcount_t seq;
|
||||
* bool X = true, Y = false;
|
||||
*
|
||||
|
@ -262,18 +291,20 @@ static inline void raw_write_seqcount_end(seqcount_t *s)
|
|||
*
|
||||
* void write(void)
|
||||
* {
|
||||
* Y = true;
|
||||
* WRITE_ONCE(Y, true);
|
||||
*
|
||||
* raw_write_seqcount_barrier(seq);
|
||||
*
|
||||
* X = false;
|
||||
* WRITE_ONCE(X, false);
|
||||
* }
|
||||
*/
|
||||
static inline void raw_write_seqcount_barrier(seqcount_t *s)
|
||||
{
|
||||
kcsan_nestable_atomic_begin();
|
||||
s->sequence++;
|
||||
smp_wmb();
|
||||
s->sequence++;
|
||||
kcsan_nestable_atomic_end();
|
||||
}
|
||||
|
||||
static inline int raw_read_seqcount_latch(seqcount_t *s)
|
||||
|
@ -398,7 +429,9 @@ static inline void write_seqcount_end(seqcount_t *s)
|
|||
static inline void write_seqcount_invalidate(seqcount_t *s)
|
||||
{
|
||||
smp_wmb();
|
||||
kcsan_nestable_atomic_begin();
|
||||
s->sequence+=2;
|
||||
kcsan_nestable_atomic_end();
|
||||
}
|
||||
|
||||
typedef struct {
|
||||
|
@ -430,11 +463,21 @@ typedef struct {
|
|||
*/
|
||||
static inline unsigned read_seqbegin(const seqlock_t *sl)
|
||||
{
|
||||
return read_seqcount_begin(&sl->seqcount);
|
||||
unsigned ret = read_seqcount_begin(&sl->seqcount);
|
||||
|
||||
kcsan_atomic_next(0); /* non-raw usage, assume closing read_seqretry() */
|
||||
kcsan_flat_atomic_begin();
|
||||
return ret;
|
||||
}
|
||||
|
||||
static inline unsigned read_seqretry(const seqlock_t *sl, unsigned start)
|
||||
{
|
||||
/*
|
||||
* Assume not nested: read_seqretry() may be called multiple times when
|
||||
* completing read critical section.
|
||||
*/
|
||||
kcsan_flat_atomic_end();
|
||||
|
||||
return read_seqcount_retry(&sl->seqcount, start);
|
||||
}
|
||||
|
||||
|
|
|
@ -2,9 +2,9 @@
|
|||
#ifndef __LINUX_UACCESS_H__
|
||||
#define __LINUX_UACCESS_H__
|
||||
|
||||
#include <linux/instrumented.h>
|
||||
#include <linux/sched.h>
|
||||
#include <linux/thread_info.h>
|
||||
#include <linux/kasan-checks.h>
|
||||
|
||||
#define uaccess_kernel() segment_eq(get_fs(), KERNEL_DS)
|
||||
|
||||
|
@ -58,7 +58,7 @@
|
|||
static __always_inline __must_check unsigned long
|
||||
__copy_from_user_inatomic(void *to, const void __user *from, unsigned long n)
|
||||
{
|
||||
kasan_check_write(to, n);
|
||||
instrument_copy_from_user(to, from, n);
|
||||
check_object_size(to, n, false);
|
||||
return raw_copy_from_user(to, from, n);
|
||||
}
|
||||
|
@ -67,7 +67,7 @@ static __always_inline __must_check unsigned long
|
|||
__copy_from_user(void *to, const void __user *from, unsigned long n)
|
||||
{
|
||||
might_fault();
|
||||
kasan_check_write(to, n);
|
||||
instrument_copy_from_user(to, from, n);
|
||||
check_object_size(to, n, false);
|
||||
return raw_copy_from_user(to, from, n);
|
||||
}
|
||||
|
@ -88,7 +88,7 @@ __copy_from_user(void *to, const void __user *from, unsigned long n)
|
|||
static __always_inline __must_check unsigned long
|
||||
__copy_to_user_inatomic(void __user *to, const void *from, unsigned long n)
|
||||
{
|
||||
kasan_check_read(from, n);
|
||||
instrument_copy_to_user(to, from, n);
|
||||
check_object_size(from, n, true);
|
||||
return raw_copy_to_user(to, from, n);
|
||||
}
|
||||
|
@ -97,7 +97,7 @@ static __always_inline __must_check unsigned long
|
|||
__copy_to_user(void __user *to, const void *from, unsigned long n)
|
||||
{
|
||||
might_fault();
|
||||
kasan_check_read(from, n);
|
||||
instrument_copy_to_user(to, from, n);
|
||||
check_object_size(from, n, true);
|
||||
return raw_copy_to_user(to, from, n);
|
||||
}
|
||||
|
@ -109,7 +109,7 @@ _copy_from_user(void *to, const void __user *from, unsigned long n)
|
|||
unsigned long res = n;
|
||||
might_fault();
|
||||
if (likely(access_ok(from, n))) {
|
||||
kasan_check_write(to, n);
|
||||
instrument_copy_from_user(to, from, n);
|
||||
res = raw_copy_from_user(to, from, n);
|
||||
}
|
||||
if (unlikely(res))
|
||||
|
@ -127,7 +127,7 @@ _copy_to_user(void __user *to, const void *from, unsigned long n)
|
|||
{
|
||||
might_fault();
|
||||
if (access_ok(to, n)) {
|
||||
kasan_check_read(from, n);
|
||||
instrument_copy_to_user(to, from, n);
|
||||
n = raw_copy_to_user(to, from, n);
|
||||
}
|
||||
return n;
|
||||
|
|
|
@ -174,6 +174,16 @@ struct task_struct init_task
|
|||
#ifdef CONFIG_KASAN
|
||||
.kasan_depth = 1,
|
||||
#endif
|
||||
#ifdef CONFIG_KCSAN
|
||||
.kcsan_ctx = {
|
||||
.disable_count = 0,
|
||||
.atomic_next = 0,
|
||||
.atomic_nest_count = 0,
|
||||
.in_flat_atomic = false,
|
||||
.access_mask = 0,
|
||||
.scoped_accesses = {LIST_POISON1, NULL},
|
||||
},
|
||||
#endif
|
||||
#ifdef CONFIG_TRACE_IRQFLAGS
|
||||
.softirqs_enabled = 1,
|
||||
#endif
|
||||
|
|
|
@ -95,6 +95,7 @@
|
|||
#include <linux/rodata_test.h>
|
||||
#include <linux/jump_label.h>
|
||||
#include <linux/mem_encrypt.h>
|
||||
#include <linux/kcsan.h>
|
||||
|
||||
#include <asm/io.h>
|
||||
#include <asm/bugs.h>
|
||||
|
@ -1036,6 +1037,7 @@ asmlinkage __visible void __init start_kernel(void)
|
|||
acpi_subsystem_init();
|
||||
arch_post_acpi_subsys_init();
|
||||
sfi_init_late();
|
||||
kcsan_init();
|
||||
|
||||
/* Do the rest non-__init'ed, we're now alive */
|
||||
arch_call_rest_init();
|
||||
|
|
|
@ -23,6 +23,9 @@ endif
|
|||
# Prevents flicker of uninteresting __do_softirq()/__local_bh_disable_ip()
|
||||
# in coverage traces.
|
||||
KCOV_INSTRUMENT_softirq.o := n
|
||||
# Avoid KCSAN instrumentation in softirq ("No shared variables, all the data
|
||||
# are CPU local" => assume no data races), to reduce overhead in interrupts.
|
||||
KCSAN_SANITIZE_softirq.o = n
|
||||
# These are called from save_stack_trace() on slub debug path,
|
||||
# and produce insane amounts of uninteresting coverage.
|
||||
KCOV_INSTRUMENT_module.o := n
|
||||
|
@ -31,6 +34,7 @@ KCOV_INSTRUMENT_stacktrace.o := n
|
|||
# Don't self-instrument.
|
||||
KCOV_INSTRUMENT_kcov.o := n
|
||||
KASAN_SANITIZE_kcov.o := n
|
||||
KCSAN_SANITIZE_kcov.o := n
|
||||
CFLAGS_kcov.o := $(call cc-option, -fno-conserve-stack -fno-stack-protector)
|
||||
|
||||
# cond_syscall is currently not LTO compatible
|
||||
|
@ -103,6 +107,7 @@ obj-$(CONFIG_TRACEPOINTS) += trace/
|
|||
obj-$(CONFIG_IRQ_WORK) += irq_work.o
|
||||
obj-$(CONFIG_CPU_PM) += cpu_pm.o
|
||||
obj-$(CONFIG_BPF) += bpf/
|
||||
obj-$(CONFIG_KCSAN) += kcsan/
|
||||
obj-$(CONFIG_SHADOW_CALL_STACK) += scs.o
|
||||
|
||||
obj-$(CONFIG_PERF_EVENTS) += events/
|
||||
|
@ -121,6 +126,7 @@ obj-$(CONFIG_SYSCTL_KUNIT_TEST) += sysctl-test.o
|
|||
|
||||
obj-$(CONFIG_GCC_PLUGIN_STACKLEAK) += stackleak.o
|
||||
KASAN_SANITIZE_stackleak.o := n
|
||||
KCSAN_SANITIZE_stackleak.o := n
|
||||
KCOV_INSTRUMENT_stackleak.o := n
|
||||
|
||||
$(obj)/configs.o: $(obj)/config_data.gz
|
||||
|
|
|
@ -0,0 +1,14 @@
|
|||
# SPDX-License-Identifier: GPL-2.0
|
||||
KCSAN_SANITIZE := n
|
||||
KCOV_INSTRUMENT := n
|
||||
UBSAN_SANITIZE := n
|
||||
|
||||
CFLAGS_REMOVE_core.o = $(CC_FLAGS_FTRACE)
|
||||
CFLAGS_REMOVE_debugfs.o = $(CC_FLAGS_FTRACE)
|
||||
CFLAGS_REMOVE_report.o = $(CC_FLAGS_FTRACE)
|
||||
|
||||
CFLAGS_core.o := $(call cc-option,-fno-conserve-stack,) \
|
||||
$(call cc-option,-fno-stack-protector,)
|
||||
|
||||
obj-y := core.o debugfs.o report.o
|
||||
obj-$(CONFIG_KCSAN_SELFTEST) += test.o
|
|
@ -0,0 +1,20 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
|
||||
#ifndef _KERNEL_KCSAN_ATOMIC_H
|
||||
#define _KERNEL_KCSAN_ATOMIC_H
|
||||
|
||||
#include <linux/jiffies.h>
|
||||
#include <linux/sched.h>
|
||||
|
||||
/*
|
||||
* Special rules for certain memory where concurrent conflicting accesses are
|
||||
* common, however, the current convention is to not mark them; returns true if
|
||||
* access to @ptr should be considered atomic. Called from slow-path.
|
||||
*/
|
||||
static bool kcsan_is_atomic_special(const volatile void *ptr)
|
||||
{
|
||||
/* volatile globals that have been observed in data races. */
|
||||
return ptr == &jiffies || ptr == ¤t->state;
|
||||
}
|
||||
|
||||
#endif /* _KERNEL_KCSAN_ATOMIC_H */
|
|
@ -0,0 +1,850 @@
|
|||
// SPDX-License-Identifier: GPL-2.0
|
||||
|
||||
#include <linux/atomic.h>
|
||||
#include <linux/bug.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/export.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/list.h>
|
||||
#include <linux/moduleparam.h>
|
||||
#include <linux/percpu.h>
|
||||
#include <linux/preempt.h>
|
||||
#include <linux/random.h>
|
||||
#include <linux/sched.h>
|
||||
#include <linux/uaccess.h>
|
||||
|
||||
#include "atomic.h"
|
||||
#include "encoding.h"
|
||||
#include "kcsan.h"
|
||||
|
||||
static bool kcsan_early_enable = IS_ENABLED(CONFIG_KCSAN_EARLY_ENABLE);
|
||||
unsigned int kcsan_udelay_task = CONFIG_KCSAN_UDELAY_TASK;
|
||||
unsigned int kcsan_udelay_interrupt = CONFIG_KCSAN_UDELAY_INTERRUPT;
|
||||
static long kcsan_skip_watch = CONFIG_KCSAN_SKIP_WATCH;
|
||||
static bool kcsan_interrupt_watcher = IS_ENABLED(CONFIG_KCSAN_INTERRUPT_WATCHER);
|
||||
|
||||
#ifdef MODULE_PARAM_PREFIX
|
||||
#undef MODULE_PARAM_PREFIX
|
||||
#endif
|
||||
#define MODULE_PARAM_PREFIX "kcsan."
|
||||
module_param_named(early_enable, kcsan_early_enable, bool, 0);
|
||||
module_param_named(udelay_task, kcsan_udelay_task, uint, 0644);
|
||||
module_param_named(udelay_interrupt, kcsan_udelay_interrupt, uint, 0644);
|
||||
module_param_named(skip_watch, kcsan_skip_watch, long, 0644);
|
||||
module_param_named(interrupt_watcher, kcsan_interrupt_watcher, bool, 0444);
|
||||
|
||||
bool kcsan_enabled;
|
||||
|
||||
/* Per-CPU kcsan_ctx for interrupts */
|
||||
static DEFINE_PER_CPU(struct kcsan_ctx, kcsan_cpu_ctx) = {
|
||||
.disable_count = 0,
|
||||
.atomic_next = 0,
|
||||
.atomic_nest_count = 0,
|
||||
.in_flat_atomic = false,
|
||||
.access_mask = 0,
|
||||
.scoped_accesses = {LIST_POISON1, NULL},
|
||||
};
|
||||
|
||||
/*
|
||||
* Helper macros to index into adjacent slots, starting from address slot
|
||||
* itself, followed by the right and left slots.
|
||||
*
|
||||
* The purpose is 2-fold:
|
||||
*
|
||||
* 1. if during insertion the address slot is already occupied, check if
|
||||
* any adjacent slots are free;
|
||||
* 2. accesses that straddle a slot boundary due to size that exceeds a
|
||||
* slot's range may check adjacent slots if any watchpoint matches.
|
||||
*
|
||||
* Note that accesses with very large size may still miss a watchpoint; however,
|
||||
* given this should be rare, this is a reasonable trade-off to make, since this
|
||||
* will avoid:
|
||||
*
|
||||
* 1. excessive contention between watchpoint checks and setup;
|
||||
* 2. larger number of simultaneous watchpoints without sacrificing
|
||||
* performance.
|
||||
*
|
||||
* Example: SLOT_IDX values for KCSAN_CHECK_ADJACENT=1, where i is [0, 1, 2]:
|
||||
*
|
||||
* slot=0: [ 1, 2, 0]
|
||||
* slot=9: [10, 11, 9]
|
||||
* slot=63: [64, 65, 63]
|
||||
*/
|
||||
#define SLOT_IDX(slot, i) (slot + ((i + KCSAN_CHECK_ADJACENT) % NUM_SLOTS))
|
||||
|
||||
/*
|
||||
* SLOT_IDX_FAST is used in the fast-path. Not first checking the address's primary
|
||||
* slot (middle) is fine if we assume that races occur rarely. The set of
|
||||
* indices {SLOT_IDX(slot, i) | i in [0, NUM_SLOTS)} is equivalent to
|
||||
* {SLOT_IDX_FAST(slot, i) | i in [0, NUM_SLOTS)}.
|
||||
*/
|
||||
#define SLOT_IDX_FAST(slot, i) (slot + i)
|
||||
|
||||
/*
|
||||
* Watchpoints, with each entry encoded as defined in encoding.h: in order to be
|
||||
* able to safely update and access a watchpoint without introducing locking
|
||||
* overhead, we encode each watchpoint as a single atomic long. The initial
|
||||
* zero-initialized state matches INVALID_WATCHPOINT.
|
||||
*
|
||||
* Add NUM_SLOTS-1 entries to account for overflow; this helps avoid having to
|
||||
* use more complicated SLOT_IDX_FAST calculation with modulo in the fast-path.
|
||||
*/
|
||||
static atomic_long_t watchpoints[CONFIG_KCSAN_NUM_WATCHPOINTS + NUM_SLOTS-1];
|
||||
|
||||
/*
|
||||
* Instructions to skip watching counter, used in should_watch(). We use a
|
||||
* per-CPU counter to avoid excessive contention.
|
||||
*/
|
||||
static DEFINE_PER_CPU(long, kcsan_skip);
|
||||
|
||||
static __always_inline atomic_long_t *find_watchpoint(unsigned long addr,
|
||||
size_t size,
|
||||
bool expect_write,
|
||||
long *encoded_watchpoint)
|
||||
{
|
||||
const int slot = watchpoint_slot(addr);
|
||||
const unsigned long addr_masked = addr & WATCHPOINT_ADDR_MASK;
|
||||
atomic_long_t *watchpoint;
|
||||
unsigned long wp_addr_masked;
|
||||
size_t wp_size;
|
||||
bool is_write;
|
||||
int i;
|
||||
|
||||
BUILD_BUG_ON(CONFIG_KCSAN_NUM_WATCHPOINTS < NUM_SLOTS);
|
||||
|
||||
for (i = 0; i < NUM_SLOTS; ++i) {
|
||||
watchpoint = &watchpoints[SLOT_IDX_FAST(slot, i)];
|
||||
*encoded_watchpoint = atomic_long_read(watchpoint);
|
||||
if (!decode_watchpoint(*encoded_watchpoint, &wp_addr_masked,
|
||||
&wp_size, &is_write))
|
||||
continue;
|
||||
|
||||
if (expect_write && !is_write)
|
||||
continue;
|
||||
|
||||
/* Check if the watchpoint matches the access. */
|
||||
if (matching_access(wp_addr_masked, wp_size, addr_masked, size))
|
||||
return watchpoint;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static inline atomic_long_t *
|
||||
insert_watchpoint(unsigned long addr, size_t size, bool is_write)
|
||||
{
|
||||
const int slot = watchpoint_slot(addr);
|
||||
const long encoded_watchpoint = encode_watchpoint(addr, size, is_write);
|
||||
atomic_long_t *watchpoint;
|
||||
int i;
|
||||
|
||||
/* Check slot index logic, ensuring we stay within array bounds. */
|
||||
BUILD_BUG_ON(SLOT_IDX(0, 0) != KCSAN_CHECK_ADJACENT);
|
||||
BUILD_BUG_ON(SLOT_IDX(0, KCSAN_CHECK_ADJACENT+1) != 0);
|
||||
BUILD_BUG_ON(SLOT_IDX(CONFIG_KCSAN_NUM_WATCHPOINTS-1, KCSAN_CHECK_ADJACENT) != ARRAY_SIZE(watchpoints)-1);
|
||||
BUILD_BUG_ON(SLOT_IDX(CONFIG_KCSAN_NUM_WATCHPOINTS-1, KCSAN_CHECK_ADJACENT+1) != ARRAY_SIZE(watchpoints) - NUM_SLOTS);
|
||||
|
||||
for (i = 0; i < NUM_SLOTS; ++i) {
|
||||
long expect_val = INVALID_WATCHPOINT;
|
||||
|
||||
/* Try to acquire this slot. */
|
||||
watchpoint = &watchpoints[SLOT_IDX(slot, i)];
|
||||
if (atomic_long_try_cmpxchg_relaxed(watchpoint, &expect_val, encoded_watchpoint))
|
||||
return watchpoint;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* Return true if watchpoint was successfully consumed, false otherwise.
|
||||
*
|
||||
* This may return false if:
|
||||
*
|
||||
* 1. another thread already consumed the watchpoint;
|
||||
* 2. the thread that set up the watchpoint already removed it;
|
||||
* 3. the watchpoint was removed and then re-used.
|
||||
*/
|
||||
static __always_inline bool
|
||||
try_consume_watchpoint(atomic_long_t *watchpoint, long encoded_watchpoint)
|
||||
{
|
||||
return atomic_long_try_cmpxchg_relaxed(watchpoint, &encoded_watchpoint, CONSUMED_WATCHPOINT);
|
||||
}
|
||||
|
||||
/* Return true if watchpoint was not touched, false if already consumed. */
|
||||
static inline bool consume_watchpoint(atomic_long_t *watchpoint)
|
||||
{
|
||||
return atomic_long_xchg_relaxed(watchpoint, CONSUMED_WATCHPOINT) != CONSUMED_WATCHPOINT;
|
||||
}
|
||||
|
||||
/* Remove the watchpoint -- its slot may be reused after. */
|
||||
static inline void remove_watchpoint(atomic_long_t *watchpoint)
|
||||
{
|
||||
atomic_long_set(watchpoint, INVALID_WATCHPOINT);
|
||||
}
|
||||
|
||||
static __always_inline struct kcsan_ctx *get_ctx(void)
|
||||
{
|
||||
/*
|
||||
* In interrupts, use raw_cpu_ptr to avoid unnecessary checks, that would
|
||||
* also result in calls that generate warnings in uaccess regions.
|
||||
*/
|
||||
return in_task() ? ¤t->kcsan_ctx : raw_cpu_ptr(&kcsan_cpu_ctx);
|
||||
}
|
||||
|
||||
/* Check scoped accesses; never inline because this is a slow-path! */
|
||||
static noinline void kcsan_check_scoped_accesses(void)
|
||||
{
|
||||
struct kcsan_ctx *ctx = get_ctx();
|
||||
struct list_head *prev_save = ctx->scoped_accesses.prev;
|
||||
struct kcsan_scoped_access *scoped_access;
|
||||
|
||||
ctx->scoped_accesses.prev = NULL; /* Avoid recursion. */
|
||||
list_for_each_entry(scoped_access, &ctx->scoped_accesses, list)
|
||||
__kcsan_check_access(scoped_access->ptr, scoped_access->size, scoped_access->type);
|
||||
ctx->scoped_accesses.prev = prev_save;
|
||||
}
|
||||
|
||||
/* Rules for generic atomic accesses. Called from fast-path. */
|
||||
static __always_inline bool
|
||||
is_atomic(const volatile void *ptr, size_t size, int type, struct kcsan_ctx *ctx)
|
||||
{
|
||||
if (type & KCSAN_ACCESS_ATOMIC)
|
||||
return true;
|
||||
|
||||
/*
|
||||
* Unless explicitly declared atomic, never consider an assertion access
|
||||
* as atomic. This allows using them also in atomic regions, such as
|
||||
* seqlocks, without implicitly changing their semantics.
|
||||
*/
|
||||
if (type & KCSAN_ACCESS_ASSERT)
|
||||
return false;
|
||||
|
||||
if (IS_ENABLED(CONFIG_KCSAN_ASSUME_PLAIN_WRITES_ATOMIC) &&
|
||||
(type & KCSAN_ACCESS_WRITE) && size <= sizeof(long) &&
|
||||
IS_ALIGNED((unsigned long)ptr, size))
|
||||
return true; /* Assume aligned writes up to word size are atomic. */
|
||||
|
||||
if (ctx->atomic_next > 0) {
|
||||
/*
|
||||
* Because we do not have separate contexts for nested
|
||||
* interrupts, in case atomic_next is set, we simply assume that
|
||||
* the outer interrupt set atomic_next. In the worst case, we
|
||||
* will conservatively consider operations as atomic. This is a
|
||||
* reasonable trade-off to make, since this case should be
|
||||
* extremely rare; however, even if extremely rare, it could
|
||||
* lead to false positives otherwise.
|
||||
*/
|
||||
if ((hardirq_count() >> HARDIRQ_SHIFT) < 2)
|
||||
--ctx->atomic_next; /* in task, or outer interrupt */
|
||||
return true;
|
||||
}
|
||||
|
||||
return ctx->atomic_nest_count > 0 || ctx->in_flat_atomic;
|
||||
}
|
||||
|
||||
static __always_inline bool
|
||||
should_watch(const volatile void *ptr, size_t size, int type, struct kcsan_ctx *ctx)
|
||||
{
|
||||
/*
|
||||
* Never set up watchpoints when memory operations are atomic.
|
||||
*
|
||||
* Need to check this first, before kcsan_skip check below: (1) atomics
|
||||
* should not count towards skipped instructions, and (2) to actually
|
||||
* decrement kcsan_atomic_next for consecutive instruction stream.
|
||||
*/
|
||||
if (is_atomic(ptr, size, type, ctx))
|
||||
return false;
|
||||
|
||||
if (this_cpu_dec_return(kcsan_skip) >= 0)
|
||||
return false;
|
||||
|
||||
/*
|
||||
* NOTE: If we get here, kcsan_skip must always be reset in slow path
|
||||
* via reset_kcsan_skip() to avoid underflow.
|
||||
*/
|
||||
|
||||
/* this operation should be watched */
|
||||
return true;
|
||||
}
|
||||
|
||||
static inline void reset_kcsan_skip(void)
|
||||
{
|
||||
long skip_count = kcsan_skip_watch -
|
||||
(IS_ENABLED(CONFIG_KCSAN_SKIP_WATCH_RANDOMIZE) ?
|
||||
prandom_u32_max(kcsan_skip_watch) :
|
||||
0);
|
||||
this_cpu_write(kcsan_skip, skip_count);
|
||||
}
|
||||
|
||||
static __always_inline bool kcsan_is_enabled(void)
|
||||
{
|
||||
return READ_ONCE(kcsan_enabled) && get_ctx()->disable_count == 0;
|
||||
}
|
||||
|
||||
static inline unsigned int get_delay(void)
|
||||
{
|
||||
unsigned int delay = in_task() ? kcsan_udelay_task : kcsan_udelay_interrupt;
|
||||
return delay - (IS_ENABLED(CONFIG_KCSAN_DELAY_RANDOMIZE) ?
|
||||
prandom_u32_max(delay) :
|
||||
0);
|
||||
}
|
||||
|
||||
/*
|
||||
* Pull everything together: check_access() below contains the performance
|
||||
* critical operations; the fast-path (including check_access) functions should
|
||||
* all be inlinable by the instrumentation functions.
|
||||
*
|
||||
* The slow-path (kcsan_found_watchpoint, kcsan_setup_watchpoint) are
|
||||
* non-inlinable -- note that, we prefix these with "kcsan_" to ensure they can
|
||||
* be filtered from the stacktrace, as well as give them unique names for the
|
||||
* UACCESS whitelist of objtool. Each function uses user_access_save/restore(),
|
||||
* since they do not access any user memory, but instrumentation is still
|
||||
* emitted in UACCESS regions.
|
||||
*/
|
||||
|
||||
static noinline void kcsan_found_watchpoint(const volatile void *ptr,
|
||||
size_t size,
|
||||
int type,
|
||||
atomic_long_t *watchpoint,
|
||||
long encoded_watchpoint)
|
||||
{
|
||||
unsigned long flags;
|
||||
bool consumed;
|
||||
|
||||
if (!kcsan_is_enabled())
|
||||
return;
|
||||
|
||||
/*
|
||||
* The access_mask check relies on value-change comparison. To avoid
|
||||
* reporting a race where e.g. the writer set up the watchpoint, but the
|
||||
* reader has access_mask!=0, we have to ignore the found watchpoint.
|
||||
*/
|
||||
if (get_ctx()->access_mask != 0)
|
||||
return;
|
||||
|
||||
/*
|
||||
* Consume the watchpoint as soon as possible, to minimize the chances
|
||||
* of !consumed. Consuming the watchpoint must always be guarded by
|
||||
* kcsan_is_enabled() check, as otherwise we might erroneously
|
||||
* triggering reports when disabled.
|
||||
*/
|
||||
consumed = try_consume_watchpoint(watchpoint, encoded_watchpoint);
|
||||
|
||||
/* keep this after try_consume_watchpoint */
|
||||
flags = user_access_save();
|
||||
|
||||
if (consumed) {
|
||||
kcsan_report(ptr, size, type, KCSAN_VALUE_CHANGE_MAYBE,
|
||||
KCSAN_REPORT_CONSUMED_WATCHPOINT,
|
||||
watchpoint - watchpoints);
|
||||
} else {
|
||||
/*
|
||||
* The other thread may not print any diagnostics, as it has
|
||||
* already removed the watchpoint, or another thread consumed
|
||||
* the watchpoint before this thread.
|
||||
*/
|
||||
kcsan_counter_inc(KCSAN_COUNTER_REPORT_RACES);
|
||||
}
|
||||
|
||||
if ((type & KCSAN_ACCESS_ASSERT) != 0)
|
||||
kcsan_counter_inc(KCSAN_COUNTER_ASSERT_FAILURES);
|
||||
else
|
||||
kcsan_counter_inc(KCSAN_COUNTER_DATA_RACES);
|
||||
|
||||
user_access_restore(flags);
|
||||
}
|
||||
|
||||
static noinline void
|
||||
kcsan_setup_watchpoint(const volatile void *ptr, size_t size, int type)
|
||||
{
|
||||
const bool is_write = (type & KCSAN_ACCESS_WRITE) != 0;
|
||||
const bool is_assert = (type & KCSAN_ACCESS_ASSERT) != 0;
|
||||
atomic_long_t *watchpoint;
|
||||
union {
|
||||
u8 _1;
|
||||
u16 _2;
|
||||
u32 _4;
|
||||
u64 _8;
|
||||
} expect_value;
|
||||
unsigned long access_mask;
|
||||
enum kcsan_value_change value_change = KCSAN_VALUE_CHANGE_MAYBE;
|
||||
unsigned long ua_flags = user_access_save();
|
||||
unsigned long irq_flags = 0;
|
||||
|
||||
/*
|
||||
* Always reset kcsan_skip counter in slow-path to avoid underflow; see
|
||||
* should_watch().
|
||||
*/
|
||||
reset_kcsan_skip();
|
||||
|
||||
if (!kcsan_is_enabled())
|
||||
goto out;
|
||||
|
||||
/*
|
||||
* Special atomic rules: unlikely to be true, so we check them here in
|
||||
* the slow-path, and not in the fast-path in is_atomic(). Call after
|
||||
* kcsan_is_enabled(), as we may access memory that is not yet
|
||||
* initialized during early boot.
|
||||
*/
|
||||
if (!is_assert && kcsan_is_atomic_special(ptr))
|
||||
goto out;
|
||||
|
||||
if (!check_encodable((unsigned long)ptr, size)) {
|
||||
kcsan_counter_inc(KCSAN_COUNTER_UNENCODABLE_ACCESSES);
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (!kcsan_interrupt_watcher)
|
||||
/* Use raw to avoid lockdep recursion via IRQ flags tracing. */
|
||||
raw_local_irq_save(irq_flags);
|
||||
|
||||
watchpoint = insert_watchpoint((unsigned long)ptr, size, is_write);
|
||||
if (watchpoint == NULL) {
|
||||
/*
|
||||
* Out of capacity: the size of 'watchpoints', and the frequency
|
||||
* with which should_watch() returns true should be tweaked so
|
||||
* that this case happens very rarely.
|
||||
*/
|
||||
kcsan_counter_inc(KCSAN_COUNTER_NO_CAPACITY);
|
||||
goto out_unlock;
|
||||
}
|
||||
|
||||
kcsan_counter_inc(KCSAN_COUNTER_SETUP_WATCHPOINTS);
|
||||
kcsan_counter_inc(KCSAN_COUNTER_USED_WATCHPOINTS);
|
||||
|
||||
/*
|
||||
* Read the current value, to later check and infer a race if the data
|
||||
* was modified via a non-instrumented access, e.g. from a device.
|
||||
*/
|
||||
expect_value._8 = 0;
|
||||
switch (size) {
|
||||
case 1:
|
||||
expect_value._1 = READ_ONCE(*(const u8 *)ptr);
|
||||
break;
|
||||
case 2:
|
||||
expect_value._2 = READ_ONCE(*(const u16 *)ptr);
|
||||
break;
|
||||
case 4:
|
||||
expect_value._4 = READ_ONCE(*(const u32 *)ptr);
|
||||
break;
|
||||
case 8:
|
||||
expect_value._8 = READ_ONCE(*(const u64 *)ptr);
|
||||
break;
|
||||
default:
|
||||
break; /* ignore; we do not diff the values */
|
||||
}
|
||||
|
||||
if (IS_ENABLED(CONFIG_KCSAN_DEBUG)) {
|
||||
kcsan_disable_current();
|
||||
pr_err("KCSAN: watching %s, size: %zu, addr: %px [slot: %d, encoded: %lx]\n",
|
||||
is_write ? "write" : "read", size, ptr,
|
||||
watchpoint_slot((unsigned long)ptr),
|
||||
encode_watchpoint((unsigned long)ptr, size, is_write));
|
||||
kcsan_enable_current();
|
||||
}
|
||||
|
||||
/*
|
||||
* Delay this thread, to increase probability of observing a racy
|
||||
* conflicting access.
|
||||
*/
|
||||
udelay(get_delay());
|
||||
|
||||
/*
|
||||
* Re-read value, and check if it is as expected; if not, we infer a
|
||||
* racy access.
|
||||
*/
|
||||
access_mask = get_ctx()->access_mask;
|
||||
switch (size) {
|
||||
case 1:
|
||||
expect_value._1 ^= READ_ONCE(*(const u8 *)ptr);
|
||||
if (access_mask)
|
||||
expect_value._1 &= (u8)access_mask;
|
||||
break;
|
||||
case 2:
|
||||
expect_value._2 ^= READ_ONCE(*(const u16 *)ptr);
|
||||
if (access_mask)
|
||||
expect_value._2 &= (u16)access_mask;
|
||||
break;
|
||||
case 4:
|
||||
expect_value._4 ^= READ_ONCE(*(const u32 *)ptr);
|
||||
if (access_mask)
|
||||
expect_value._4 &= (u32)access_mask;
|
||||
break;
|
||||
case 8:
|
||||
expect_value._8 ^= READ_ONCE(*(const u64 *)ptr);
|
||||
if (access_mask)
|
||||
expect_value._8 &= (u64)access_mask;
|
||||
break;
|
||||
default:
|
||||
break; /* ignore; we do not diff the values */
|
||||
}
|
||||
|
||||
/* Were we able to observe a value-change? */
|
||||
if (expect_value._8 != 0)
|
||||
value_change = KCSAN_VALUE_CHANGE_TRUE;
|
||||
|
||||
/* Check if this access raced with another. */
|
||||
if (!consume_watchpoint(watchpoint)) {
|
||||
/*
|
||||
* Depending on the access type, map a value_change of MAYBE to
|
||||
* TRUE (always report) or FALSE (never report).
|
||||
*/
|
||||
if (value_change == KCSAN_VALUE_CHANGE_MAYBE) {
|
||||
if (access_mask != 0) {
|
||||
/*
|
||||
* For access with access_mask, we require a
|
||||
* value-change, as it is likely that races on
|
||||
* ~access_mask bits are expected.
|
||||
*/
|
||||
value_change = KCSAN_VALUE_CHANGE_FALSE;
|
||||
} else if (size > 8 || is_assert) {
|
||||
/* Always assume a value-change. */
|
||||
value_change = KCSAN_VALUE_CHANGE_TRUE;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* No need to increment 'data_races' counter, as the racing
|
||||
* thread already did.
|
||||
*
|
||||
* Count 'assert_failures' for each failed ASSERT access,
|
||||
* therefore both this thread and the racing thread may
|
||||
* increment this counter.
|
||||
*/
|
||||
if (is_assert && value_change == KCSAN_VALUE_CHANGE_TRUE)
|
||||
kcsan_counter_inc(KCSAN_COUNTER_ASSERT_FAILURES);
|
||||
|
||||
kcsan_report(ptr, size, type, value_change, KCSAN_REPORT_RACE_SIGNAL,
|
||||
watchpoint - watchpoints);
|
||||
} else if (value_change == KCSAN_VALUE_CHANGE_TRUE) {
|
||||
/* Inferring a race, since the value should not have changed. */
|
||||
|
||||
kcsan_counter_inc(KCSAN_COUNTER_RACES_UNKNOWN_ORIGIN);
|
||||
if (is_assert)
|
||||
kcsan_counter_inc(KCSAN_COUNTER_ASSERT_FAILURES);
|
||||
|
||||
if (IS_ENABLED(CONFIG_KCSAN_REPORT_RACE_UNKNOWN_ORIGIN) || is_assert)
|
||||
kcsan_report(ptr, size, type, KCSAN_VALUE_CHANGE_TRUE,
|
||||
KCSAN_REPORT_RACE_UNKNOWN_ORIGIN,
|
||||
watchpoint - watchpoints);
|
||||
}
|
||||
|
||||
/*
|
||||
* Remove watchpoint; must be after reporting, since the slot may be
|
||||
* reused after this point.
|
||||
*/
|
||||
remove_watchpoint(watchpoint);
|
||||
kcsan_counter_dec(KCSAN_COUNTER_USED_WATCHPOINTS);
|
||||
out_unlock:
|
||||
if (!kcsan_interrupt_watcher)
|
||||
raw_local_irq_restore(irq_flags);
|
||||
out:
|
||||
user_access_restore(ua_flags);
|
||||
}
|
||||
|
||||
static __always_inline void check_access(const volatile void *ptr, size_t size,
|
||||
int type)
|
||||
{
|
||||
const bool is_write = (type & KCSAN_ACCESS_WRITE) != 0;
|
||||
atomic_long_t *watchpoint;
|
||||
long encoded_watchpoint;
|
||||
|
||||
/*
|
||||
* Do nothing for 0 sized check; this comparison will be optimized out
|
||||
* for constant sized instrumentation (__tsan_{read,write}N).
|
||||
*/
|
||||
if (unlikely(size == 0))
|
||||
return;
|
||||
|
||||
/*
|
||||
* Avoid user_access_save in fast-path: find_watchpoint is safe without
|
||||
* user_access_save, as the address that ptr points to is only used to
|
||||
* check if a watchpoint exists; ptr is never dereferenced.
|
||||
*/
|
||||
watchpoint = find_watchpoint((unsigned long)ptr, size, !is_write,
|
||||
&encoded_watchpoint);
|
||||
/*
|
||||
* It is safe to check kcsan_is_enabled() after find_watchpoint in the
|
||||
* slow-path, as long as no state changes that cause a race to be
|
||||
* detected and reported have occurred until kcsan_is_enabled() is
|
||||
* checked.
|
||||
*/
|
||||
|
||||
if (unlikely(watchpoint != NULL))
|
||||
kcsan_found_watchpoint(ptr, size, type, watchpoint,
|
||||
encoded_watchpoint);
|
||||
else {
|
||||
struct kcsan_ctx *ctx = get_ctx(); /* Call only once in fast-path. */
|
||||
|
||||
if (unlikely(should_watch(ptr, size, type, ctx)))
|
||||
kcsan_setup_watchpoint(ptr, size, type);
|
||||
else if (unlikely(ctx->scoped_accesses.prev))
|
||||
kcsan_check_scoped_accesses();
|
||||
}
|
||||
}
|
||||
|
||||
/* === Public interface ===================================================== */
|
||||
|
||||
void __init kcsan_init(void)
|
||||
{
|
||||
BUG_ON(!in_task());
|
||||
|
||||
kcsan_debugfs_init();
|
||||
|
||||
/*
|
||||
* We are in the init task, and no other tasks should be running;
|
||||
* WRITE_ONCE without memory barrier is sufficient.
|
||||
*/
|
||||
if (kcsan_early_enable)
|
||||
WRITE_ONCE(kcsan_enabled, true);
|
||||
}
|
||||
|
||||
/* === Exported interface =================================================== */
|
||||
|
||||
void kcsan_disable_current(void)
|
||||
{
|
||||
++get_ctx()->disable_count;
|
||||
}
|
||||
EXPORT_SYMBOL(kcsan_disable_current);
|
||||
|
||||
void kcsan_enable_current(void)
|
||||
{
|
||||
if (get_ctx()->disable_count-- == 0) {
|
||||
/*
|
||||
* Warn if kcsan_enable_current() calls are unbalanced with
|
||||
* kcsan_disable_current() calls, which causes disable_count to
|
||||
* become negative and should not happen.
|
||||
*/
|
||||
kcsan_disable_current(); /* restore to 0, KCSAN still enabled */
|
||||
kcsan_disable_current(); /* disable to generate warning */
|
||||
WARN(1, "Unbalanced %s()", __func__);
|
||||
kcsan_enable_current();
|
||||
}
|
||||
}
|
||||
EXPORT_SYMBOL(kcsan_enable_current);
|
||||
|
||||
void kcsan_enable_current_nowarn(void)
|
||||
{
|
||||
if (get_ctx()->disable_count-- == 0)
|
||||
kcsan_disable_current();
|
||||
}
|
||||
EXPORT_SYMBOL(kcsan_enable_current_nowarn);
|
||||
|
||||
void kcsan_nestable_atomic_begin(void)
|
||||
{
|
||||
/*
|
||||
* Do *not* check and warn if we are in a flat atomic region: nestable
|
||||
* and flat atomic regions are independent from each other.
|
||||
* See include/linux/kcsan.h: struct kcsan_ctx comments for more
|
||||
* comments.
|
||||
*/
|
||||
|
||||
++get_ctx()->atomic_nest_count;
|
||||
}
|
||||
EXPORT_SYMBOL(kcsan_nestable_atomic_begin);
|
||||
|
||||
void kcsan_nestable_atomic_end(void)
|
||||
{
|
||||
if (get_ctx()->atomic_nest_count-- == 0) {
|
||||
/*
|
||||
* Warn if kcsan_nestable_atomic_end() calls are unbalanced with
|
||||
* kcsan_nestable_atomic_begin() calls, which causes
|
||||
* atomic_nest_count to become negative and should not happen.
|
||||
*/
|
||||
kcsan_nestable_atomic_begin(); /* restore to 0 */
|
||||
kcsan_disable_current(); /* disable to generate warning */
|
||||
WARN(1, "Unbalanced %s()", __func__);
|
||||
kcsan_enable_current();
|
||||
}
|
||||
}
|
||||
EXPORT_SYMBOL(kcsan_nestable_atomic_end);
|
||||
|
||||
void kcsan_flat_atomic_begin(void)
|
||||
{
|
||||
get_ctx()->in_flat_atomic = true;
|
||||
}
|
||||
EXPORT_SYMBOL(kcsan_flat_atomic_begin);
|
||||
|
||||
void kcsan_flat_atomic_end(void)
|
||||
{
|
||||
get_ctx()->in_flat_atomic = false;
|
||||
}
|
||||
EXPORT_SYMBOL(kcsan_flat_atomic_end);
|
||||
|
||||
void kcsan_atomic_next(int n)
|
||||
{
|
||||
get_ctx()->atomic_next = n;
|
||||
}
|
||||
EXPORT_SYMBOL(kcsan_atomic_next);
|
||||
|
||||
void kcsan_set_access_mask(unsigned long mask)
|
||||
{
|
||||
get_ctx()->access_mask = mask;
|
||||
}
|
||||
EXPORT_SYMBOL(kcsan_set_access_mask);
|
||||
|
||||
struct kcsan_scoped_access *
|
||||
kcsan_begin_scoped_access(const volatile void *ptr, size_t size, int type,
|
||||
struct kcsan_scoped_access *sa)
|
||||
{
|
||||
struct kcsan_ctx *ctx = get_ctx();
|
||||
|
||||
__kcsan_check_access(ptr, size, type);
|
||||
|
||||
ctx->disable_count++; /* Disable KCSAN, in case list debugging is on. */
|
||||
|
||||
INIT_LIST_HEAD(&sa->list);
|
||||
sa->ptr = ptr;
|
||||
sa->size = size;
|
||||
sa->type = type;
|
||||
|
||||
if (!ctx->scoped_accesses.prev) /* Lazy initialize list head. */
|
||||
INIT_LIST_HEAD(&ctx->scoped_accesses);
|
||||
list_add(&sa->list, &ctx->scoped_accesses);
|
||||
|
||||
ctx->disable_count--;
|
||||
return sa;
|
||||
}
|
||||
EXPORT_SYMBOL(kcsan_begin_scoped_access);
|
||||
|
||||
void kcsan_end_scoped_access(struct kcsan_scoped_access *sa)
|
||||
{
|
||||
struct kcsan_ctx *ctx = get_ctx();
|
||||
|
||||
if (WARN(!ctx->scoped_accesses.prev, "Unbalanced %s()?", __func__))
|
||||
return;
|
||||
|
||||
ctx->disable_count++; /* Disable KCSAN, in case list debugging is on. */
|
||||
|
||||
list_del(&sa->list);
|
||||
if (list_empty(&ctx->scoped_accesses))
|
||||
/*
|
||||
* Ensure we do not enter kcsan_check_scoped_accesses()
|
||||
* slow-path if unnecessary, and avoids requiring list_empty()
|
||||
* in the fast-path (to avoid a READ_ONCE() and potential
|
||||
* uaccess warning).
|
||||
*/
|
||||
ctx->scoped_accesses.prev = NULL;
|
||||
|
||||
ctx->disable_count--;
|
||||
|
||||
__kcsan_check_access(sa->ptr, sa->size, sa->type);
|
||||
}
|
||||
EXPORT_SYMBOL(kcsan_end_scoped_access);
|
||||
|
||||
void __kcsan_check_access(const volatile void *ptr, size_t size, int type)
|
||||
{
|
||||
check_access(ptr, size, type);
|
||||
}
|
||||
EXPORT_SYMBOL(__kcsan_check_access);
|
||||
|
||||
/*
|
||||
* KCSAN uses the same instrumentation that is emitted by supported compilers
|
||||
* for ThreadSanitizer (TSAN).
|
||||
*
|
||||
* When enabled, the compiler emits instrumentation calls (the functions
|
||||
* prefixed with "__tsan" below) for all loads and stores that it generated;
|
||||
* inline asm is not instrumented.
|
||||
*
|
||||
* Note that, not all supported compiler versions distinguish aligned/unaligned
|
||||
* accesses, but e.g. recent versions of Clang do. We simply alias the unaligned
|
||||
* version to the generic version, which can handle both.
|
||||
*/
|
||||
|
||||
#define DEFINE_TSAN_READ_WRITE(size) \
|
||||
void __tsan_read##size(void *ptr) \
|
||||
{ \
|
||||
check_access(ptr, size, 0); \
|
||||
} \
|
||||
EXPORT_SYMBOL(__tsan_read##size); \
|
||||
void __tsan_unaligned_read##size(void *ptr) \
|
||||
__alias(__tsan_read##size); \
|
||||
EXPORT_SYMBOL(__tsan_unaligned_read##size); \
|
||||
void __tsan_write##size(void *ptr) \
|
||||
{ \
|
||||
check_access(ptr, size, KCSAN_ACCESS_WRITE); \
|
||||
} \
|
||||
EXPORT_SYMBOL(__tsan_write##size); \
|
||||
void __tsan_unaligned_write##size(void *ptr) \
|
||||
__alias(__tsan_write##size); \
|
||||
EXPORT_SYMBOL(__tsan_unaligned_write##size)
|
||||
|
||||
DEFINE_TSAN_READ_WRITE(1);
|
||||
DEFINE_TSAN_READ_WRITE(2);
|
||||
DEFINE_TSAN_READ_WRITE(4);
|
||||
DEFINE_TSAN_READ_WRITE(8);
|
||||
DEFINE_TSAN_READ_WRITE(16);
|
||||
|
||||
void __tsan_read_range(void *ptr, size_t size)
|
||||
{
|
||||
check_access(ptr, size, 0);
|
||||
}
|
||||
EXPORT_SYMBOL(__tsan_read_range);
|
||||
|
||||
void __tsan_write_range(void *ptr, size_t size)
|
||||
{
|
||||
check_access(ptr, size, KCSAN_ACCESS_WRITE);
|
||||
}
|
||||
EXPORT_SYMBOL(__tsan_write_range);
|
||||
|
||||
/*
|
||||
* Use of explicit volatile is generally disallowed [1], however, volatile is
|
||||
* still used in various concurrent context, whether in low-level
|
||||
* synchronization primitives or for legacy reasons.
|
||||
* [1] https://lwn.net/Articles/233479/
|
||||
*
|
||||
* We only consider volatile accesses atomic if they are aligned and would pass
|
||||
* the size-check of compiletime_assert_rwonce_type().
|
||||
*/
|
||||
#define DEFINE_TSAN_VOLATILE_READ_WRITE(size) \
|
||||
void __tsan_volatile_read##size(void *ptr) \
|
||||
{ \
|
||||
const bool is_atomic = size <= sizeof(long long) && \
|
||||
IS_ALIGNED((unsigned long)ptr, size); \
|
||||
if (IS_ENABLED(CONFIG_KCSAN_IGNORE_ATOMICS) && is_atomic) \
|
||||
return; \
|
||||
check_access(ptr, size, is_atomic ? KCSAN_ACCESS_ATOMIC : 0); \
|
||||
} \
|
||||
EXPORT_SYMBOL(__tsan_volatile_read##size); \
|
||||
void __tsan_unaligned_volatile_read##size(void *ptr) \
|
||||
__alias(__tsan_volatile_read##size); \
|
||||
EXPORT_SYMBOL(__tsan_unaligned_volatile_read##size); \
|
||||
void __tsan_volatile_write##size(void *ptr) \
|
||||
{ \
|
||||
const bool is_atomic = size <= sizeof(long long) && \
|
||||
IS_ALIGNED((unsigned long)ptr, size); \
|
||||
if (IS_ENABLED(CONFIG_KCSAN_IGNORE_ATOMICS) && is_atomic) \
|
||||
return; \
|
||||
check_access(ptr, size, \
|
||||
KCSAN_ACCESS_WRITE | \
|
||||
(is_atomic ? KCSAN_ACCESS_ATOMIC : 0)); \
|
||||
} \
|
||||
EXPORT_SYMBOL(__tsan_volatile_write##size); \
|
||||
void __tsan_unaligned_volatile_write##size(void *ptr) \
|
||||
__alias(__tsan_volatile_write##size); \
|
||||
EXPORT_SYMBOL(__tsan_unaligned_volatile_write##size)
|
||||
|
||||
DEFINE_TSAN_VOLATILE_READ_WRITE(1);
|
||||
DEFINE_TSAN_VOLATILE_READ_WRITE(2);
|
||||
DEFINE_TSAN_VOLATILE_READ_WRITE(4);
|
||||
DEFINE_TSAN_VOLATILE_READ_WRITE(8);
|
||||
DEFINE_TSAN_VOLATILE_READ_WRITE(16);
|
||||
|
||||
/*
|
||||
* The below are not required by KCSAN, but can still be emitted by the
|
||||
* compiler.
|
||||
*/
|
||||
void __tsan_func_entry(void *call_pc)
|
||||
{
|
||||
}
|
||||
EXPORT_SYMBOL(__tsan_func_entry);
|
||||
void __tsan_func_exit(void)
|
||||
{
|
||||
}
|
||||
EXPORT_SYMBOL(__tsan_func_exit);
|
||||
void __tsan_init(void)
|
||||
{
|
||||
}
|
||||
EXPORT_SYMBOL(__tsan_init);
|
|
@ -0,0 +1,349 @@
|
|||
// SPDX-License-Identifier: GPL-2.0
|
||||
|
||||
#include <linux/atomic.h>
|
||||
#include <linux/bsearch.h>
|
||||
#include <linux/bug.h>
|
||||
#include <linux/debugfs.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/kallsyms.h>
|
||||
#include <linux/sched.h>
|
||||
#include <linux/seq_file.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/sort.h>
|
||||
#include <linux/string.h>
|
||||
#include <linux/uaccess.h>
|
||||
|
||||
#include "kcsan.h"
|
||||
|
||||
/*
|
||||
* Statistics counters.
|
||||
*/
|
||||
static atomic_long_t counters[KCSAN_COUNTER_COUNT];
|
||||
|
||||
/*
|
||||
* Addresses for filtering functions from reporting. This list can be used as a
|
||||
* whitelist or blacklist.
|
||||
*/
|
||||
static struct {
|
||||
unsigned long *addrs; /* array of addresses */
|
||||
size_t size; /* current size */
|
||||
int used; /* number of elements used */
|
||||
bool sorted; /* if elements are sorted */
|
||||
bool whitelist; /* if list is a blacklist or whitelist */
|
||||
} report_filterlist = {
|
||||
.addrs = NULL,
|
||||
.size = 8, /* small initial size */
|
||||
.used = 0,
|
||||
.sorted = false,
|
||||
.whitelist = false, /* default is blacklist */
|
||||
};
|
||||
static DEFINE_SPINLOCK(report_filterlist_lock);
|
||||
|
||||
static const char *counter_to_name(enum kcsan_counter_id id)
|
||||
{
|
||||
switch (id) {
|
||||
case KCSAN_COUNTER_USED_WATCHPOINTS: return "used_watchpoints";
|
||||
case KCSAN_COUNTER_SETUP_WATCHPOINTS: return "setup_watchpoints";
|
||||
case KCSAN_COUNTER_DATA_RACES: return "data_races";
|
||||
case KCSAN_COUNTER_ASSERT_FAILURES: return "assert_failures";
|
||||
case KCSAN_COUNTER_NO_CAPACITY: return "no_capacity";
|
||||
case KCSAN_COUNTER_REPORT_RACES: return "report_races";
|
||||
case KCSAN_COUNTER_RACES_UNKNOWN_ORIGIN: return "races_unknown_origin";
|
||||
case KCSAN_COUNTER_UNENCODABLE_ACCESSES: return "unencodable_accesses";
|
||||
case KCSAN_COUNTER_ENCODING_FALSE_POSITIVES: return "encoding_false_positives";
|
||||
case KCSAN_COUNTER_COUNT:
|
||||
BUG();
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void kcsan_counter_inc(enum kcsan_counter_id id)
|
||||
{
|
||||
atomic_long_inc(&counters[id]);
|
||||
}
|
||||
|
||||
void kcsan_counter_dec(enum kcsan_counter_id id)
|
||||
{
|
||||
atomic_long_dec(&counters[id]);
|
||||
}
|
||||
|
||||
/*
|
||||
* The microbenchmark allows benchmarking KCSAN core runtime only. To run
|
||||
* multiple threads, pipe 'microbench=<iters>' from multiple tasks into the
|
||||
* debugfs file. This will not generate any conflicts, and tests fast-path only.
|
||||
*/
|
||||
static noinline void microbenchmark(unsigned long iters)
|
||||
{
|
||||
const struct kcsan_ctx ctx_save = current->kcsan_ctx;
|
||||
const bool was_enabled = READ_ONCE(kcsan_enabled);
|
||||
cycles_t cycles;
|
||||
|
||||
/* We may have been called from an atomic region; reset context. */
|
||||
memset(¤t->kcsan_ctx, 0, sizeof(current->kcsan_ctx));
|
||||
/*
|
||||
* Disable to benchmark fast-path for all accesses, and (expected
|
||||
* negligible) call into slow-path, but never set up watchpoints.
|
||||
*/
|
||||
WRITE_ONCE(kcsan_enabled, false);
|
||||
|
||||
pr_info("KCSAN: %s begin | iters: %lu\n", __func__, iters);
|
||||
|
||||
cycles = get_cycles();
|
||||
while (iters--) {
|
||||
unsigned long addr = iters & ((PAGE_SIZE << 8) - 1);
|
||||
int type = !(iters & 0x7f) ? KCSAN_ACCESS_ATOMIC :
|
||||
(!(iters & 0xf) ? KCSAN_ACCESS_WRITE : 0);
|
||||
__kcsan_check_access((void *)addr, sizeof(long), type);
|
||||
}
|
||||
cycles = get_cycles() - cycles;
|
||||
|
||||
pr_info("KCSAN: %s end | cycles: %llu\n", __func__, cycles);
|
||||
|
||||
WRITE_ONCE(kcsan_enabled, was_enabled);
|
||||
/* restore context */
|
||||
current->kcsan_ctx = ctx_save;
|
||||
}
|
||||
|
||||
/*
|
||||
* Simple test to create conflicting accesses. Write 'test=<iters>' to KCSAN's
|
||||
* debugfs file from multiple tasks to generate real conflicts and show reports.
|
||||
*/
|
||||
static long test_dummy;
|
||||
static long test_flags;
|
||||
static long test_scoped;
|
||||
static noinline void test_thread(unsigned long iters)
|
||||
{
|
||||
const long CHANGE_BITS = 0xff00ff00ff00ff00L;
|
||||
const struct kcsan_ctx ctx_save = current->kcsan_ctx;
|
||||
cycles_t cycles;
|
||||
|
||||
/* We may have been called from an atomic region; reset context. */
|
||||
memset(¤t->kcsan_ctx, 0, sizeof(current->kcsan_ctx));
|
||||
|
||||
pr_info("KCSAN: %s begin | iters: %lu\n", __func__, iters);
|
||||
pr_info("test_dummy@%px, test_flags@%px, test_scoped@%px,\n",
|
||||
&test_dummy, &test_flags, &test_scoped);
|
||||
|
||||
cycles = get_cycles();
|
||||
while (iters--) {
|
||||
/* These all should generate reports. */
|
||||
__kcsan_check_read(&test_dummy, sizeof(test_dummy));
|
||||
ASSERT_EXCLUSIVE_WRITER(test_dummy);
|
||||
ASSERT_EXCLUSIVE_ACCESS(test_dummy);
|
||||
|
||||
ASSERT_EXCLUSIVE_BITS(test_flags, ~CHANGE_BITS); /* no report */
|
||||
__kcsan_check_read(&test_flags, sizeof(test_flags)); /* no report */
|
||||
|
||||
ASSERT_EXCLUSIVE_BITS(test_flags, CHANGE_BITS); /* report */
|
||||
__kcsan_check_read(&test_flags, sizeof(test_flags)); /* no report */
|
||||
|
||||
/* not actually instrumented */
|
||||
WRITE_ONCE(test_dummy, iters); /* to observe value-change */
|
||||
__kcsan_check_write(&test_dummy, sizeof(test_dummy));
|
||||
|
||||
test_flags ^= CHANGE_BITS; /* generate value-change */
|
||||
__kcsan_check_write(&test_flags, sizeof(test_flags));
|
||||
|
||||
BUG_ON(current->kcsan_ctx.scoped_accesses.prev);
|
||||
{
|
||||
/* Should generate reports anywhere in this block. */
|
||||
ASSERT_EXCLUSIVE_WRITER_SCOPED(test_scoped);
|
||||
ASSERT_EXCLUSIVE_ACCESS_SCOPED(test_scoped);
|
||||
BUG_ON(!current->kcsan_ctx.scoped_accesses.prev);
|
||||
/* Unrelated accesses. */
|
||||
__kcsan_check_access(&cycles, sizeof(cycles), 0);
|
||||
__kcsan_check_access(&cycles, sizeof(cycles), KCSAN_ACCESS_ATOMIC);
|
||||
}
|
||||
BUG_ON(current->kcsan_ctx.scoped_accesses.prev);
|
||||
}
|
||||
cycles = get_cycles() - cycles;
|
||||
|
||||
pr_info("KCSAN: %s end | cycles: %llu\n", __func__, cycles);
|
||||
|
||||
/* restore context */
|
||||
current->kcsan_ctx = ctx_save;
|
||||
}
|
||||
|
||||
static int cmp_filterlist_addrs(const void *rhs, const void *lhs)
|
||||
{
|
||||
const unsigned long a = *(const unsigned long *)rhs;
|
||||
const unsigned long b = *(const unsigned long *)lhs;
|
||||
|
||||
return a < b ? -1 : a == b ? 0 : 1;
|
||||
}
|
||||
|
||||
bool kcsan_skip_report_debugfs(unsigned long func_addr)
|
||||
{
|
||||
unsigned long symbolsize, offset;
|
||||
unsigned long flags;
|
||||
bool ret = false;
|
||||
|
||||
if (!kallsyms_lookup_size_offset(func_addr, &symbolsize, &offset))
|
||||
return false;
|
||||
func_addr -= offset; /* Get function start */
|
||||
|
||||
spin_lock_irqsave(&report_filterlist_lock, flags);
|
||||
if (report_filterlist.used == 0)
|
||||
goto out;
|
||||
|
||||
/* Sort array if it is unsorted, and then do a binary search. */
|
||||
if (!report_filterlist.sorted) {
|
||||
sort(report_filterlist.addrs, report_filterlist.used,
|
||||
sizeof(unsigned long), cmp_filterlist_addrs, NULL);
|
||||
report_filterlist.sorted = true;
|
||||
}
|
||||
ret = !!bsearch(&func_addr, report_filterlist.addrs,
|
||||
report_filterlist.used, sizeof(unsigned long),
|
||||
cmp_filterlist_addrs);
|
||||
if (report_filterlist.whitelist)
|
||||
ret = !ret;
|
||||
|
||||
out:
|
||||
spin_unlock_irqrestore(&report_filterlist_lock, flags);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void set_report_filterlist_whitelist(bool whitelist)
|
||||
{
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&report_filterlist_lock, flags);
|
||||
report_filterlist.whitelist = whitelist;
|
||||
spin_unlock_irqrestore(&report_filterlist_lock, flags);
|
||||
}
|
||||
|
||||
/* Returns 0 on success, error-code otherwise. */
|
||||
static ssize_t insert_report_filterlist(const char *func)
|
||||
{
|
||||
unsigned long flags;
|
||||
unsigned long addr = kallsyms_lookup_name(func);
|
||||
ssize_t ret = 0;
|
||||
|
||||
if (!addr) {
|
||||
pr_err("KCSAN: could not find function: '%s'\n", func);
|
||||
return -ENOENT;
|
||||
}
|
||||
|
||||
spin_lock_irqsave(&report_filterlist_lock, flags);
|
||||
|
||||
if (report_filterlist.addrs == NULL) {
|
||||
/* initial allocation */
|
||||
report_filterlist.addrs =
|
||||
kmalloc_array(report_filterlist.size,
|
||||
sizeof(unsigned long), GFP_ATOMIC);
|
||||
if (report_filterlist.addrs == NULL) {
|
||||
ret = -ENOMEM;
|
||||
goto out;
|
||||
}
|
||||
} else if (report_filterlist.used == report_filterlist.size) {
|
||||
/* resize filterlist */
|
||||
size_t new_size = report_filterlist.size * 2;
|
||||
unsigned long *new_addrs =
|
||||
krealloc(report_filterlist.addrs,
|
||||
new_size * sizeof(unsigned long), GFP_ATOMIC);
|
||||
|
||||
if (new_addrs == NULL) {
|
||||
/* leave filterlist itself untouched */
|
||||
ret = -ENOMEM;
|
||||
goto out;
|
||||
}
|
||||
|
||||
report_filterlist.size = new_size;
|
||||
report_filterlist.addrs = new_addrs;
|
||||
}
|
||||
|
||||
/* Note: deduplicating should be done in userspace. */
|
||||
report_filterlist.addrs[report_filterlist.used++] =
|
||||
kallsyms_lookup_name(func);
|
||||
report_filterlist.sorted = false;
|
||||
|
||||
out:
|
||||
spin_unlock_irqrestore(&report_filterlist_lock, flags);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int show_info(struct seq_file *file, void *v)
|
||||
{
|
||||
int i;
|
||||
unsigned long flags;
|
||||
|
||||
/* show stats */
|
||||
seq_printf(file, "enabled: %i\n", READ_ONCE(kcsan_enabled));
|
||||
for (i = 0; i < KCSAN_COUNTER_COUNT; ++i)
|
||||
seq_printf(file, "%s: %ld\n", counter_to_name(i),
|
||||
atomic_long_read(&counters[i]));
|
||||
|
||||
/* show filter functions, and filter type */
|
||||
spin_lock_irqsave(&report_filterlist_lock, flags);
|
||||
seq_printf(file, "\n%s functions: %s\n",
|
||||
report_filterlist.whitelist ? "whitelisted" : "blacklisted",
|
||||
report_filterlist.used == 0 ? "none" : "");
|
||||
for (i = 0; i < report_filterlist.used; ++i)
|
||||
seq_printf(file, " %ps\n", (void *)report_filterlist.addrs[i]);
|
||||
spin_unlock_irqrestore(&report_filterlist_lock, flags);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int debugfs_open(struct inode *inode, struct file *file)
|
||||
{
|
||||
return single_open(file, show_info, NULL);
|
||||
}
|
||||
|
||||
static ssize_t
|
||||
debugfs_write(struct file *file, const char __user *buf, size_t count, loff_t *off)
|
||||
{
|
||||
char kbuf[KSYM_NAME_LEN];
|
||||
char *arg;
|
||||
int read_len = count < (sizeof(kbuf) - 1) ? count : (sizeof(kbuf) - 1);
|
||||
|
||||
if (copy_from_user(kbuf, buf, read_len))
|
||||
return -EFAULT;
|
||||
kbuf[read_len] = '\0';
|
||||
arg = strstrip(kbuf);
|
||||
|
||||
if (!strcmp(arg, "on")) {
|
||||
WRITE_ONCE(kcsan_enabled, true);
|
||||
} else if (!strcmp(arg, "off")) {
|
||||
WRITE_ONCE(kcsan_enabled, false);
|
||||
} else if (!strncmp(arg, "microbench=", sizeof("microbench=") - 1)) {
|
||||
unsigned long iters;
|
||||
|
||||
if (kstrtoul(&arg[sizeof("microbench=") - 1], 0, &iters))
|
||||
return -EINVAL;
|
||||
microbenchmark(iters);
|
||||
} else if (!strncmp(arg, "test=", sizeof("test=") - 1)) {
|
||||
unsigned long iters;
|
||||
|
||||
if (kstrtoul(&arg[sizeof("test=") - 1], 0, &iters))
|
||||
return -EINVAL;
|
||||
test_thread(iters);
|
||||
} else if (!strcmp(arg, "whitelist")) {
|
||||
set_report_filterlist_whitelist(true);
|
||||
} else if (!strcmp(arg, "blacklist")) {
|
||||
set_report_filterlist_whitelist(false);
|
||||
} else if (arg[0] == '!') {
|
||||
ssize_t ret = insert_report_filterlist(&arg[1]);
|
||||
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
} else {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
static const struct file_operations debugfs_ops =
|
||||
{
|
||||
.read = seq_read,
|
||||
.open = debugfs_open,
|
||||
.write = debugfs_write,
|
||||
.release = single_release
|
||||
};
|
||||
|
||||
void __init kcsan_debugfs_init(void)
|
||||
{
|
||||
debugfs_create_file("kcsan", 0644, NULL, NULL, &debugfs_ops);
|
||||
}
|
|
@ -0,0 +1,95 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
|
||||
#ifndef _KERNEL_KCSAN_ENCODING_H
|
||||
#define _KERNEL_KCSAN_ENCODING_H
|
||||
|
||||
#include <linux/bits.h>
|
||||
#include <linux/log2.h>
|
||||
#include <linux/mm.h>
|
||||
|
||||
#include "kcsan.h"
|
||||
|
||||
#define SLOT_RANGE PAGE_SIZE
|
||||
|
||||
#define INVALID_WATCHPOINT 0
|
||||
#define CONSUMED_WATCHPOINT 1
|
||||
|
||||
/*
|
||||
* The maximum useful size of accesses for which we set up watchpoints is the
|
||||
* max range of slots we check on an access.
|
||||
*/
|
||||
#define MAX_ENCODABLE_SIZE (SLOT_RANGE * (1 + KCSAN_CHECK_ADJACENT))
|
||||
|
||||
/*
|
||||
* Number of bits we use to store size info.
|
||||
*/
|
||||
#define WATCHPOINT_SIZE_BITS bits_per(MAX_ENCODABLE_SIZE)
|
||||
/*
|
||||
* This encoding for addresses discards the upper (1 for is-write + SIZE_BITS);
|
||||
* however, most 64-bit architectures do not use the full 64-bit address space.
|
||||
* Also, in order for a false positive to be observable 2 things need to happen:
|
||||
*
|
||||
* 1. different addresses but with the same encoded address race;
|
||||
* 2. and both map onto the same watchpoint slots;
|
||||
*
|
||||
* Both these are assumed to be very unlikely. However, in case it still happens
|
||||
* happens, the report logic will filter out the false positive (see report.c).
|
||||
*/
|
||||
#define WATCHPOINT_ADDR_BITS (BITS_PER_LONG-1 - WATCHPOINT_SIZE_BITS)
|
||||
|
||||
/*
|
||||
* Masks to set/retrieve the encoded data.
|
||||
*/
|
||||
#define WATCHPOINT_WRITE_MASK BIT(BITS_PER_LONG-1)
|
||||
#define WATCHPOINT_SIZE_MASK \
|
||||
GENMASK(BITS_PER_LONG-2, BITS_PER_LONG-2 - WATCHPOINT_SIZE_BITS)
|
||||
#define WATCHPOINT_ADDR_MASK \
|
||||
GENMASK(BITS_PER_LONG-3 - WATCHPOINT_SIZE_BITS, 0)
|
||||
|
||||
static inline bool check_encodable(unsigned long addr, size_t size)
|
||||
{
|
||||
return size <= MAX_ENCODABLE_SIZE;
|
||||
}
|
||||
|
||||
static inline long
|
||||
encode_watchpoint(unsigned long addr, size_t size, bool is_write)
|
||||
{
|
||||
return (long)((is_write ? WATCHPOINT_WRITE_MASK : 0) |
|
||||
(size << WATCHPOINT_ADDR_BITS) |
|
||||
(addr & WATCHPOINT_ADDR_MASK));
|
||||
}
|
||||
|
||||
static __always_inline bool decode_watchpoint(long watchpoint,
|
||||
unsigned long *addr_masked,
|
||||
size_t *size,
|
||||
bool *is_write)
|
||||
{
|
||||
if (watchpoint == INVALID_WATCHPOINT ||
|
||||
watchpoint == CONSUMED_WATCHPOINT)
|
||||
return false;
|
||||
|
||||
*addr_masked = (unsigned long)watchpoint & WATCHPOINT_ADDR_MASK;
|
||||
*size = ((unsigned long)watchpoint & WATCHPOINT_SIZE_MASK) >> WATCHPOINT_ADDR_BITS;
|
||||
*is_write = !!((unsigned long)watchpoint & WATCHPOINT_WRITE_MASK);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
* Return watchpoint slot for an address.
|
||||
*/
|
||||
static __always_inline int watchpoint_slot(unsigned long addr)
|
||||
{
|
||||
return (addr / PAGE_SIZE) % CONFIG_KCSAN_NUM_WATCHPOINTS;
|
||||
}
|
||||
|
||||
static __always_inline bool matching_access(unsigned long addr1, size_t size1,
|
||||
unsigned long addr2, size_t size2)
|
||||
{
|
||||
unsigned long end_range1 = addr1 + size1 - 1;
|
||||
unsigned long end_range2 = addr2 + size2 - 1;
|
||||
|
||||
return addr1 <= end_range2 && addr2 <= end_range1;
|
||||
}
|
||||
|
||||
#endif /* _KERNEL_KCSAN_ENCODING_H */
|
|
@ -0,0 +1,142 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
|
||||
/*
|
||||
* The Kernel Concurrency Sanitizer (KCSAN) infrastructure. For more info please
|
||||
* see Documentation/dev-tools/kcsan.rst.
|
||||
*/
|
||||
|
||||
#ifndef _KERNEL_KCSAN_KCSAN_H
|
||||
#define _KERNEL_KCSAN_KCSAN_H
|
||||
|
||||
#include <linux/kcsan.h>
|
||||
|
||||
/* The number of adjacent watchpoints to check. */
|
||||
#define KCSAN_CHECK_ADJACENT 1
|
||||
#define NUM_SLOTS (1 + 2*KCSAN_CHECK_ADJACENT)
|
||||
|
||||
extern unsigned int kcsan_udelay_task;
|
||||
extern unsigned int kcsan_udelay_interrupt;
|
||||
|
||||
/*
|
||||
* Globally enable and disable KCSAN.
|
||||
*/
|
||||
extern bool kcsan_enabled;
|
||||
|
||||
/*
|
||||
* Initialize debugfs file.
|
||||
*/
|
||||
void kcsan_debugfs_init(void);
|
||||
|
||||
enum kcsan_counter_id {
|
||||
/*
|
||||
* Number of watchpoints currently in use.
|
||||
*/
|
||||
KCSAN_COUNTER_USED_WATCHPOINTS,
|
||||
|
||||
/*
|
||||
* Total number of watchpoints set up.
|
||||
*/
|
||||
KCSAN_COUNTER_SETUP_WATCHPOINTS,
|
||||
|
||||
/*
|
||||
* Total number of data races.
|
||||
*/
|
||||
KCSAN_COUNTER_DATA_RACES,
|
||||
|
||||
/*
|
||||
* Total number of ASSERT failures due to races. If the observed race is
|
||||
* due to two conflicting ASSERT type accesses, then both will be
|
||||
* counted.
|
||||
*/
|
||||
KCSAN_COUNTER_ASSERT_FAILURES,
|
||||
|
||||
/*
|
||||
* Number of times no watchpoints were available.
|
||||
*/
|
||||
KCSAN_COUNTER_NO_CAPACITY,
|
||||
|
||||
/*
|
||||
* A thread checking a watchpoint raced with another checking thread;
|
||||
* only one will be reported.
|
||||
*/
|
||||
KCSAN_COUNTER_REPORT_RACES,
|
||||
|
||||
/*
|
||||
* Observed data value change, but writer thread unknown.
|
||||
*/
|
||||
KCSAN_COUNTER_RACES_UNKNOWN_ORIGIN,
|
||||
|
||||
/*
|
||||
* The access cannot be encoded to a valid watchpoint.
|
||||
*/
|
||||
KCSAN_COUNTER_UNENCODABLE_ACCESSES,
|
||||
|
||||
/*
|
||||
* Watchpoint encoding caused a watchpoint to fire on mismatching
|
||||
* accesses.
|
||||
*/
|
||||
KCSAN_COUNTER_ENCODING_FALSE_POSITIVES,
|
||||
|
||||
KCSAN_COUNTER_COUNT, /* number of counters */
|
||||
};
|
||||
|
||||
/*
|
||||
* Increment/decrement counter with given id; avoid calling these in fast-path.
|
||||
*/
|
||||
extern void kcsan_counter_inc(enum kcsan_counter_id id);
|
||||
extern void kcsan_counter_dec(enum kcsan_counter_id id);
|
||||
|
||||
/*
|
||||
* Returns true if data races in the function symbol that maps to func_addr
|
||||
* (offsets are ignored) should *not* be reported.
|
||||
*/
|
||||
extern bool kcsan_skip_report_debugfs(unsigned long func_addr);
|
||||
|
||||
/*
|
||||
* Value-change states.
|
||||
*/
|
||||
enum kcsan_value_change {
|
||||
/*
|
||||
* Did not observe a value-change, however, it is valid to report the
|
||||
* race, depending on preferences.
|
||||
*/
|
||||
KCSAN_VALUE_CHANGE_MAYBE,
|
||||
|
||||
/*
|
||||
* Did not observe a value-change, and it is invalid to report the race.
|
||||
*/
|
||||
KCSAN_VALUE_CHANGE_FALSE,
|
||||
|
||||
/*
|
||||
* The value was observed to change, and the race should be reported.
|
||||
*/
|
||||
KCSAN_VALUE_CHANGE_TRUE,
|
||||
};
|
||||
|
||||
enum kcsan_report_type {
|
||||
/*
|
||||
* The thread that set up the watchpoint and briefly stalled was
|
||||
* signalled that another thread triggered the watchpoint.
|
||||
*/
|
||||
KCSAN_REPORT_RACE_SIGNAL,
|
||||
|
||||
/*
|
||||
* A thread found and consumed a matching watchpoint.
|
||||
*/
|
||||
KCSAN_REPORT_CONSUMED_WATCHPOINT,
|
||||
|
||||
/*
|
||||
* No other thread was observed to race with the access, but the data
|
||||
* value before and after the stall differs.
|
||||
*/
|
||||
KCSAN_REPORT_RACE_UNKNOWN_ORIGIN,
|
||||
};
|
||||
|
||||
/*
|
||||
* Print a race report from thread that encountered the race.
|
||||
*/
|
||||
extern void kcsan_report(const volatile void *ptr, size_t size, int access_type,
|
||||
enum kcsan_value_change value_change,
|
||||
enum kcsan_report_type type, int watchpoint_idx);
|
||||
|
||||
#endif /* _KERNEL_KCSAN_KCSAN_H */
|
|
@ -0,0 +1,634 @@
|
|||
// SPDX-License-Identifier: GPL-2.0
|
||||
|
||||
#include <linux/debug_locks.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/jiffies.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/lockdep.h>
|
||||
#include <linux/preempt.h>
|
||||
#include <linux/printk.h>
|
||||
#include <linux/sched.h>
|
||||
#include <linux/spinlock.h>
|
||||
#include <linux/stacktrace.h>
|
||||
|
||||
#include "kcsan.h"
|
||||
#include "encoding.h"
|
||||
|
||||
/*
|
||||
* Max. number of stack entries to show in the report.
|
||||
*/
|
||||
#define NUM_STACK_ENTRIES 64
|
||||
|
||||
/* Common access info. */
|
||||
struct access_info {
|
||||
const volatile void *ptr;
|
||||
size_t size;
|
||||
int access_type;
|
||||
int task_pid;
|
||||
int cpu_id;
|
||||
};
|
||||
|
||||
/*
|
||||
* Other thread info: communicated from other racing thread to thread that set
|
||||
* up the watchpoint, which then prints the complete report atomically.
|
||||
*/
|
||||
struct other_info {
|
||||
struct access_info ai;
|
||||
unsigned long stack_entries[NUM_STACK_ENTRIES];
|
||||
int num_stack_entries;
|
||||
|
||||
/*
|
||||
* Optionally pass @current. Typically we do not need to pass @current
|
||||
* via @other_info since just @task_pid is sufficient. Passing @current
|
||||
* has additional overhead.
|
||||
*
|
||||
* To safely pass @current, we must either use get_task_struct/
|
||||
* put_task_struct, or stall the thread that populated @other_info.
|
||||
*
|
||||
* We cannot rely on get_task_struct/put_task_struct in case
|
||||
* release_report() races with a task being released, and would have to
|
||||
* free it in release_report(). This may result in deadlock if we want
|
||||
* to use KCSAN on the allocators.
|
||||
*
|
||||
* Since we also want to reliably print held locks for
|
||||
* CONFIG_KCSAN_VERBOSE, the current implementation stalls the thread
|
||||
* that populated @other_info until it has been consumed.
|
||||
*/
|
||||
struct task_struct *task;
|
||||
};
|
||||
|
||||
/*
|
||||
* To never block any producers of struct other_info, we need as many elements
|
||||
* as we have watchpoints (upper bound on concurrent races to report).
|
||||
*/
|
||||
static struct other_info other_infos[CONFIG_KCSAN_NUM_WATCHPOINTS + NUM_SLOTS-1];
|
||||
|
||||
/*
|
||||
* Information about reported races; used to rate limit reporting.
|
||||
*/
|
||||
struct report_time {
|
||||
/*
|
||||
* The last time the race was reported.
|
||||
*/
|
||||
unsigned long time;
|
||||
|
||||
/*
|
||||
* The frames of the 2 threads; if only 1 thread is known, one frame
|
||||
* will be 0.
|
||||
*/
|
||||
unsigned long frame1;
|
||||
unsigned long frame2;
|
||||
};
|
||||
|
||||
/*
|
||||
* Since we also want to be able to debug allocators with KCSAN, to avoid
|
||||
* deadlock, report_times cannot be dynamically resized with krealloc in
|
||||
* rate_limit_report.
|
||||
*
|
||||
* Therefore, we use a fixed-size array, which at most will occupy a page. This
|
||||
* still adequately rate limits reports, assuming that a) number of unique data
|
||||
* races is not excessive, and b) occurrence of unique races within the
|
||||
* same time window is limited.
|
||||
*/
|
||||
#define REPORT_TIMES_MAX (PAGE_SIZE / sizeof(struct report_time))
|
||||
#define REPORT_TIMES_SIZE \
|
||||
(CONFIG_KCSAN_REPORT_ONCE_IN_MS > REPORT_TIMES_MAX ? \
|
||||
REPORT_TIMES_MAX : \
|
||||
CONFIG_KCSAN_REPORT_ONCE_IN_MS)
|
||||
static struct report_time report_times[REPORT_TIMES_SIZE];
|
||||
|
||||
/*
|
||||
* Spinlock serializing report generation, and access to @other_infos. Although
|
||||
* it could make sense to have a finer-grained locking story for @other_infos,
|
||||
* report generation needs to be serialized either way, so not much is gained.
|
||||
*/
|
||||
static DEFINE_RAW_SPINLOCK(report_lock);
|
||||
|
||||
/*
|
||||
* Checks if the race identified by thread frames frame1 and frame2 has
|
||||
* been reported since (now - KCSAN_REPORT_ONCE_IN_MS).
|
||||
*/
|
||||
static bool rate_limit_report(unsigned long frame1, unsigned long frame2)
|
||||
{
|
||||
struct report_time *use_entry = &report_times[0];
|
||||
unsigned long invalid_before;
|
||||
int i;
|
||||
|
||||
BUILD_BUG_ON(CONFIG_KCSAN_REPORT_ONCE_IN_MS != 0 && REPORT_TIMES_SIZE == 0);
|
||||
|
||||
if (CONFIG_KCSAN_REPORT_ONCE_IN_MS == 0)
|
||||
return false;
|
||||
|
||||
invalid_before = jiffies - msecs_to_jiffies(CONFIG_KCSAN_REPORT_ONCE_IN_MS);
|
||||
|
||||
/* Check if a matching race report exists. */
|
||||
for (i = 0; i < REPORT_TIMES_SIZE; ++i) {
|
||||
struct report_time *rt = &report_times[i];
|
||||
|
||||
/*
|
||||
* Must always select an entry for use to store info as we
|
||||
* cannot resize report_times; at the end of the scan, use_entry
|
||||
* will be the oldest entry, which ideally also happened before
|
||||
* KCSAN_REPORT_ONCE_IN_MS ago.
|
||||
*/
|
||||
if (time_before(rt->time, use_entry->time))
|
||||
use_entry = rt;
|
||||
|
||||
/*
|
||||
* Initially, no need to check any further as this entry as well
|
||||
* as following entries have never been used.
|
||||
*/
|
||||
if (rt->time == 0)
|
||||
break;
|
||||
|
||||
/* Check if entry expired. */
|
||||
if (time_before(rt->time, invalid_before))
|
||||
continue; /* before KCSAN_REPORT_ONCE_IN_MS ago */
|
||||
|
||||
/* Reported recently, check if race matches. */
|
||||
if ((rt->frame1 == frame1 && rt->frame2 == frame2) ||
|
||||
(rt->frame1 == frame2 && rt->frame2 == frame1))
|
||||
return true;
|
||||
}
|
||||
|
||||
use_entry->time = jiffies;
|
||||
use_entry->frame1 = frame1;
|
||||
use_entry->frame2 = frame2;
|
||||
return false;
|
||||
}
|
||||
|
||||
/*
|
||||
* Special rules to skip reporting.
|
||||
*/
|
||||
static bool
|
||||
skip_report(enum kcsan_value_change value_change, unsigned long top_frame)
|
||||
{
|
||||
/* Should never get here if value_change==FALSE. */
|
||||
WARN_ON_ONCE(value_change == KCSAN_VALUE_CHANGE_FALSE);
|
||||
|
||||
/*
|
||||
* The first call to skip_report always has value_change==TRUE, since we
|
||||
* cannot know the value written of an instrumented access. For the 2nd
|
||||
* call there are 6 cases with CONFIG_KCSAN_REPORT_VALUE_CHANGE_ONLY:
|
||||
*
|
||||
* 1. read watchpoint, conflicting write (value_change==TRUE): report;
|
||||
* 2. read watchpoint, conflicting write (value_change==MAYBE): skip;
|
||||
* 3. write watchpoint, conflicting write (value_change==TRUE): report;
|
||||
* 4. write watchpoint, conflicting write (value_change==MAYBE): skip;
|
||||
* 5. write watchpoint, conflicting read (value_change==MAYBE): skip;
|
||||
* 6. write watchpoint, conflicting read (value_change==TRUE): report;
|
||||
*
|
||||
* Cases 1-4 are intuitive and expected; case 5 ensures we do not report
|
||||
* data races where the write may have rewritten the same value; case 6
|
||||
* is possible either if the size is larger than what we check value
|
||||
* changes for or the access type is KCSAN_ACCESS_ASSERT.
|
||||
*/
|
||||
if (IS_ENABLED(CONFIG_KCSAN_REPORT_VALUE_CHANGE_ONLY) &&
|
||||
value_change == KCSAN_VALUE_CHANGE_MAYBE) {
|
||||
/*
|
||||
* The access is a write, but the data value did not change.
|
||||
*
|
||||
* We opt-out of this filter for certain functions at request of
|
||||
* maintainers.
|
||||
*/
|
||||
char buf[64];
|
||||
int len = scnprintf(buf, sizeof(buf), "%ps", (void *)top_frame);
|
||||
|
||||
if (!strnstr(buf, "rcu_", len) &&
|
||||
!strnstr(buf, "_rcu", len) &&
|
||||
!strnstr(buf, "_srcu", len))
|
||||
return true;
|
||||
}
|
||||
|
||||
return kcsan_skip_report_debugfs(top_frame);
|
||||
}
|
||||
|
||||
static const char *get_access_type(int type)
|
||||
{
|
||||
if (type & KCSAN_ACCESS_ASSERT) {
|
||||
if (type & KCSAN_ACCESS_SCOPED) {
|
||||
if (type & KCSAN_ACCESS_WRITE)
|
||||
return "assert no accesses (scoped)";
|
||||
else
|
||||
return "assert no writes (scoped)";
|
||||
} else {
|
||||
if (type & KCSAN_ACCESS_WRITE)
|
||||
return "assert no accesses";
|
||||
else
|
||||
return "assert no writes";
|
||||
}
|
||||
}
|
||||
|
||||
switch (type) {
|
||||
case 0:
|
||||
return "read";
|
||||
case KCSAN_ACCESS_ATOMIC:
|
||||
return "read (marked)";
|
||||
case KCSAN_ACCESS_WRITE:
|
||||
return "write";
|
||||
case KCSAN_ACCESS_WRITE | KCSAN_ACCESS_ATOMIC:
|
||||
return "write (marked)";
|
||||
case KCSAN_ACCESS_SCOPED:
|
||||
return "read (scoped)";
|
||||
case KCSAN_ACCESS_SCOPED | KCSAN_ACCESS_ATOMIC:
|
||||
return "read (marked, scoped)";
|
||||
case KCSAN_ACCESS_SCOPED | KCSAN_ACCESS_WRITE:
|
||||
return "write (scoped)";
|
||||
case KCSAN_ACCESS_SCOPED | KCSAN_ACCESS_WRITE | KCSAN_ACCESS_ATOMIC:
|
||||
return "write (marked, scoped)";
|
||||
default:
|
||||
BUG();
|
||||
}
|
||||
}
|
||||
|
||||
static const char *get_bug_type(int type)
|
||||
{
|
||||
return (type & KCSAN_ACCESS_ASSERT) != 0 ? "assert: race" : "data-race";
|
||||
}
|
||||
|
||||
/* Return thread description: in task or interrupt. */
|
||||
static const char *get_thread_desc(int task_id)
|
||||
{
|
||||
if (task_id != -1) {
|
||||
static char buf[32]; /* safe: protected by report_lock */
|
||||
|
||||
snprintf(buf, sizeof(buf), "task %i", task_id);
|
||||
return buf;
|
||||
}
|
||||
return "interrupt";
|
||||
}
|
||||
|
||||
/* Helper to skip KCSAN-related functions in stack-trace. */
|
||||
static int get_stack_skipnr(const unsigned long stack_entries[], int num_entries)
|
||||
{
|
||||
char buf[64];
|
||||
char *cur;
|
||||
int len, skip;
|
||||
|
||||
for (skip = 0; skip < num_entries; ++skip) {
|
||||
len = scnprintf(buf, sizeof(buf), "%ps", (void *)stack_entries[skip]);
|
||||
|
||||
/* Never show tsan_* or {read,write}_once_size. */
|
||||
if (strnstr(buf, "tsan_", len) ||
|
||||
strnstr(buf, "_once_size", len))
|
||||
continue;
|
||||
|
||||
cur = strnstr(buf, "kcsan_", len);
|
||||
if (cur) {
|
||||
cur += sizeof("kcsan_") - 1;
|
||||
if (strncmp(cur, "test", sizeof("test") - 1))
|
||||
continue; /* KCSAN runtime function. */
|
||||
/* KCSAN related test. */
|
||||
}
|
||||
|
||||
/*
|
||||
* No match for runtime functions -- @skip entries to skip to
|
||||
* get to first frame of interest.
|
||||
*/
|
||||
break;
|
||||
}
|
||||
|
||||
return skip;
|
||||
}
|
||||
|
||||
/* Compares symbolized strings of addr1 and addr2. */
|
||||
static int sym_strcmp(void *addr1, void *addr2)
|
||||
{
|
||||
char buf1[64];
|
||||
char buf2[64];
|
||||
|
||||
snprintf(buf1, sizeof(buf1), "%pS", addr1);
|
||||
snprintf(buf2, sizeof(buf2), "%pS", addr2);
|
||||
|
||||
return strncmp(buf1, buf2, sizeof(buf1));
|
||||
}
|
||||
|
||||
static void print_verbose_info(struct task_struct *task)
|
||||
{
|
||||
if (!task)
|
||||
return;
|
||||
|
||||
pr_err("\n");
|
||||
debug_show_held_locks(task);
|
||||
print_irqtrace_events(task);
|
||||
}
|
||||
|
||||
/*
|
||||
* Returns true if a report was generated, false otherwise.
|
||||
*/
|
||||
static bool print_report(enum kcsan_value_change value_change,
|
||||
enum kcsan_report_type type,
|
||||
const struct access_info *ai,
|
||||
const struct other_info *other_info)
|
||||
{
|
||||
unsigned long stack_entries[NUM_STACK_ENTRIES] = { 0 };
|
||||
int num_stack_entries = stack_trace_save(stack_entries, NUM_STACK_ENTRIES, 1);
|
||||
int skipnr = get_stack_skipnr(stack_entries, num_stack_entries);
|
||||
unsigned long this_frame = stack_entries[skipnr];
|
||||
unsigned long other_frame = 0;
|
||||
int other_skipnr = 0; /* silence uninit warnings */
|
||||
|
||||
/*
|
||||
* Must check report filter rules before starting to print.
|
||||
*/
|
||||
if (skip_report(KCSAN_VALUE_CHANGE_TRUE, stack_entries[skipnr]))
|
||||
return false;
|
||||
|
||||
if (type == KCSAN_REPORT_RACE_SIGNAL) {
|
||||
other_skipnr = get_stack_skipnr(other_info->stack_entries,
|
||||
other_info->num_stack_entries);
|
||||
other_frame = other_info->stack_entries[other_skipnr];
|
||||
|
||||
/* @value_change is only known for the other thread */
|
||||
if (skip_report(value_change, other_frame))
|
||||
return false;
|
||||
}
|
||||
|
||||
if (rate_limit_report(this_frame, other_frame))
|
||||
return false;
|
||||
|
||||
/* Print report header. */
|
||||
pr_err("==================================================================\n");
|
||||
switch (type) {
|
||||
case KCSAN_REPORT_RACE_SIGNAL: {
|
||||
int cmp;
|
||||
|
||||
/*
|
||||
* Order functions lexographically for consistent bug titles.
|
||||
* Do not print offset of functions to keep title short.
|
||||
*/
|
||||
cmp = sym_strcmp((void *)other_frame, (void *)this_frame);
|
||||
pr_err("BUG: KCSAN: %s in %ps / %ps\n",
|
||||
get_bug_type(ai->access_type | other_info->ai.access_type),
|
||||
(void *)(cmp < 0 ? other_frame : this_frame),
|
||||
(void *)(cmp < 0 ? this_frame : other_frame));
|
||||
} break;
|
||||
|
||||
case KCSAN_REPORT_RACE_UNKNOWN_ORIGIN:
|
||||
pr_err("BUG: KCSAN: %s in %pS\n", get_bug_type(ai->access_type),
|
||||
(void *)this_frame);
|
||||
break;
|
||||
|
||||
default:
|
||||
BUG();
|
||||
}
|
||||
|
||||
pr_err("\n");
|
||||
|
||||
/* Print information about the racing accesses. */
|
||||
switch (type) {
|
||||
case KCSAN_REPORT_RACE_SIGNAL:
|
||||
pr_err("%s to 0x%px of %zu bytes by %s on cpu %i:\n",
|
||||
get_access_type(other_info->ai.access_type), other_info->ai.ptr,
|
||||
other_info->ai.size, get_thread_desc(other_info->ai.task_pid),
|
||||
other_info->ai.cpu_id);
|
||||
|
||||
/* Print the other thread's stack trace. */
|
||||
stack_trace_print(other_info->stack_entries + other_skipnr,
|
||||
other_info->num_stack_entries - other_skipnr,
|
||||
0);
|
||||
|
||||
if (IS_ENABLED(CONFIG_KCSAN_VERBOSE))
|
||||
print_verbose_info(other_info->task);
|
||||
|
||||
pr_err("\n");
|
||||
pr_err("%s to 0x%px of %zu bytes by %s on cpu %i:\n",
|
||||
get_access_type(ai->access_type), ai->ptr, ai->size,
|
||||
get_thread_desc(ai->task_pid), ai->cpu_id);
|
||||
break;
|
||||
|
||||
case KCSAN_REPORT_RACE_UNKNOWN_ORIGIN:
|
||||
pr_err("race at unknown origin, with %s to 0x%px of %zu bytes by %s on cpu %i:\n",
|
||||
get_access_type(ai->access_type), ai->ptr, ai->size,
|
||||
get_thread_desc(ai->task_pid), ai->cpu_id);
|
||||
break;
|
||||
|
||||
default:
|
||||
BUG();
|
||||
}
|
||||
/* Print stack trace of this thread. */
|
||||
stack_trace_print(stack_entries + skipnr, num_stack_entries - skipnr,
|
||||
0);
|
||||
|
||||
if (IS_ENABLED(CONFIG_KCSAN_VERBOSE))
|
||||
print_verbose_info(current);
|
||||
|
||||
/* Print report footer. */
|
||||
pr_err("\n");
|
||||
pr_err("Reported by Kernel Concurrency Sanitizer on:\n");
|
||||
dump_stack_print_info(KERN_DEFAULT);
|
||||
pr_err("==================================================================\n");
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static void release_report(unsigned long *flags, struct other_info *other_info)
|
||||
{
|
||||
if (other_info)
|
||||
/*
|
||||
* Use size to denote valid/invalid, since KCSAN entirely
|
||||
* ignores 0-sized accesses.
|
||||
*/
|
||||
other_info->ai.size = 0;
|
||||
|
||||
raw_spin_unlock_irqrestore(&report_lock, *flags);
|
||||
}
|
||||
|
||||
/*
|
||||
* Sets @other_info->task and awaits consumption of @other_info.
|
||||
*
|
||||
* Precondition: report_lock is held.
|
||||
* Postcondition: report_lock is held.
|
||||
*/
|
||||
static void set_other_info_task_blocking(unsigned long *flags,
|
||||
const struct access_info *ai,
|
||||
struct other_info *other_info)
|
||||
{
|
||||
/*
|
||||
* We may be instrumenting a code-path where current->state is already
|
||||
* something other than TASK_RUNNING.
|
||||
*/
|
||||
const bool is_running = current->state == TASK_RUNNING;
|
||||
/*
|
||||
* To avoid deadlock in case we are in an interrupt here and this is a
|
||||
* race with a task on the same CPU (KCSAN_INTERRUPT_WATCHER), provide a
|
||||
* timeout to ensure this works in all contexts.
|
||||
*
|
||||
* Await approximately the worst case delay of the reporting thread (if
|
||||
* we are not interrupted).
|
||||
*/
|
||||
int timeout = max(kcsan_udelay_task, kcsan_udelay_interrupt);
|
||||
|
||||
other_info->task = current;
|
||||
do {
|
||||
if (is_running) {
|
||||
/*
|
||||
* Let lockdep know the real task is sleeping, to print
|
||||
* the held locks (recall we turned lockdep off, so
|
||||
* locking/unlocking @report_lock won't be recorded).
|
||||
*/
|
||||
set_current_state(TASK_UNINTERRUPTIBLE);
|
||||
}
|
||||
raw_spin_unlock_irqrestore(&report_lock, *flags);
|
||||
/*
|
||||
* We cannot call schedule() since we also cannot reliably
|
||||
* determine if sleeping here is permitted -- see in_atomic().
|
||||
*/
|
||||
|
||||
udelay(1);
|
||||
raw_spin_lock_irqsave(&report_lock, *flags);
|
||||
if (timeout-- < 0) {
|
||||
/*
|
||||
* Abort. Reset @other_info->task to NULL, since it
|
||||
* appears the other thread is still going to consume
|
||||
* it. It will result in no verbose info printed for
|
||||
* this task.
|
||||
*/
|
||||
other_info->task = NULL;
|
||||
break;
|
||||
}
|
||||
/*
|
||||
* If invalid, or @ptr nor @current matches, then @other_info
|
||||
* has been consumed and we may continue. If not, retry.
|
||||
*/
|
||||
} while (other_info->ai.size && other_info->ai.ptr == ai->ptr &&
|
||||
other_info->task == current);
|
||||
if (is_running)
|
||||
set_current_state(TASK_RUNNING);
|
||||
}
|
||||
|
||||
/* Populate @other_info; requires that the provided @other_info not in use. */
|
||||
static void prepare_report_producer(unsigned long *flags,
|
||||
const struct access_info *ai,
|
||||
struct other_info *other_info)
|
||||
{
|
||||
raw_spin_lock_irqsave(&report_lock, *flags);
|
||||
|
||||
/*
|
||||
* The same @other_infos entry cannot be used concurrently, because
|
||||
* there is a one-to-one mapping to watchpoint slots (@watchpoints in
|
||||
* core.c), and a watchpoint is only released for reuse after reporting
|
||||
* is done by the consumer of @other_info. Therefore, it is impossible
|
||||
* for another concurrent prepare_report_producer() to set the same
|
||||
* @other_info, and are guaranteed exclusivity for the @other_infos
|
||||
* entry pointed to by @other_info.
|
||||
*
|
||||
* To check this property holds, size should never be non-zero here,
|
||||
* because every consumer of struct other_info resets size to 0 in
|
||||
* release_report().
|
||||
*/
|
||||
WARN_ON(other_info->ai.size);
|
||||
|
||||
other_info->ai = *ai;
|
||||
other_info->num_stack_entries = stack_trace_save(other_info->stack_entries, NUM_STACK_ENTRIES, 2);
|
||||
|
||||
if (IS_ENABLED(CONFIG_KCSAN_VERBOSE))
|
||||
set_other_info_task_blocking(flags, ai, other_info);
|
||||
|
||||
raw_spin_unlock_irqrestore(&report_lock, *flags);
|
||||
}
|
||||
|
||||
/* Awaits producer to fill @other_info and then returns. */
|
||||
static bool prepare_report_consumer(unsigned long *flags,
|
||||
const struct access_info *ai,
|
||||
struct other_info *other_info)
|
||||
{
|
||||
|
||||
raw_spin_lock_irqsave(&report_lock, *flags);
|
||||
while (!other_info->ai.size) { /* Await valid @other_info. */
|
||||
raw_spin_unlock_irqrestore(&report_lock, *flags);
|
||||
cpu_relax();
|
||||
raw_spin_lock_irqsave(&report_lock, *flags);
|
||||
}
|
||||
|
||||
/* Should always have a matching access based on watchpoint encoding. */
|
||||
if (WARN_ON(!matching_access((unsigned long)other_info->ai.ptr & WATCHPOINT_ADDR_MASK, other_info->ai.size,
|
||||
(unsigned long)ai->ptr & WATCHPOINT_ADDR_MASK, ai->size)))
|
||||
goto discard;
|
||||
|
||||
if (!matching_access((unsigned long)other_info->ai.ptr, other_info->ai.size,
|
||||
(unsigned long)ai->ptr, ai->size)) {
|
||||
/*
|
||||
* If the actual accesses to not match, this was a false
|
||||
* positive due to watchpoint encoding.
|
||||
*/
|
||||
kcsan_counter_inc(KCSAN_COUNTER_ENCODING_FALSE_POSITIVES);
|
||||
goto discard;
|
||||
}
|
||||
|
||||
return true;
|
||||
|
||||
discard:
|
||||
release_report(flags, other_info);
|
||||
return false;
|
||||
}
|
||||
|
||||
/*
|
||||
* Depending on the report type either sets @other_info and returns false, or
|
||||
* awaits @other_info and returns true. If @other_info is not required for the
|
||||
* report type, simply acquires @report_lock and returns true.
|
||||
*/
|
||||
static noinline bool prepare_report(unsigned long *flags,
|
||||
enum kcsan_report_type type,
|
||||
const struct access_info *ai,
|
||||
struct other_info *other_info)
|
||||
{
|
||||
switch (type) {
|
||||
case KCSAN_REPORT_CONSUMED_WATCHPOINT:
|
||||
prepare_report_producer(flags, ai, other_info);
|
||||
return false;
|
||||
case KCSAN_REPORT_RACE_SIGNAL:
|
||||
return prepare_report_consumer(flags, ai, other_info);
|
||||
default:
|
||||
/* @other_info not required; just acquire @report_lock. */
|
||||
raw_spin_lock_irqsave(&report_lock, *flags);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
void kcsan_report(const volatile void *ptr, size_t size, int access_type,
|
||||
enum kcsan_value_change value_change,
|
||||
enum kcsan_report_type type, int watchpoint_idx)
|
||||
{
|
||||
unsigned long flags = 0;
|
||||
const struct access_info ai = {
|
||||
.ptr = ptr,
|
||||
.size = size,
|
||||
.access_type = access_type,
|
||||
.task_pid = in_task() ? task_pid_nr(current) : -1,
|
||||
.cpu_id = raw_smp_processor_id()
|
||||
};
|
||||
struct other_info *other_info = type == KCSAN_REPORT_RACE_UNKNOWN_ORIGIN
|
||||
? NULL : &other_infos[watchpoint_idx];
|
||||
|
||||
kcsan_disable_current();
|
||||
if (WARN_ON(watchpoint_idx < 0 || watchpoint_idx >= ARRAY_SIZE(other_infos)))
|
||||
goto out;
|
||||
|
||||
/*
|
||||
* With TRACE_IRQFLAGS, lockdep's IRQ trace state becomes corrupted if
|
||||
* we do not turn off lockdep here; this could happen due to recursion
|
||||
* into lockdep via KCSAN if we detect a race in utilities used by
|
||||
* lockdep.
|
||||
*/
|
||||
lockdep_off();
|
||||
|
||||
if (prepare_report(&flags, type, &ai, other_info)) {
|
||||
/*
|
||||
* Never report if value_change is FALSE, only if we it is
|
||||
* either TRUE or MAYBE. In case of MAYBE, further filtering may
|
||||
* be done once we know the full stack trace in print_report().
|
||||
*/
|
||||
bool reported = value_change != KCSAN_VALUE_CHANGE_FALSE &&
|
||||
print_report(value_change, type, &ai, other_info);
|
||||
|
||||
if (reported && panic_on_warn)
|
||||
panic("panic_on_warn set ...\n");
|
||||
|
||||
release_report(&flags, other_info);
|
||||
}
|
||||
|
||||
lockdep_on();
|
||||
out:
|
||||
kcsan_enable_current();
|
||||
}
|
|
@ -0,0 +1,131 @@
|
|||
// SPDX-License-Identifier: GPL-2.0
|
||||
|
||||
#include <linux/init.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/printk.h>
|
||||
#include <linux/random.h>
|
||||
#include <linux/types.h>
|
||||
|
||||
#include "encoding.h"
|
||||
|
||||
#define ITERS_PER_TEST 2000
|
||||
|
||||
/* Test requirements. */
|
||||
static bool test_requires(void)
|
||||
{
|
||||
/* random should be initialized for the below tests */
|
||||
return prandom_u32() + prandom_u32() != 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Test watchpoint encode and decode: check that encoding some access's info,
|
||||
* and then subsequent decode preserves the access's info.
|
||||
*/
|
||||
static bool test_encode_decode(void)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < ITERS_PER_TEST; ++i) {
|
||||
size_t size = prandom_u32_max(MAX_ENCODABLE_SIZE) + 1;
|
||||
bool is_write = !!prandom_u32_max(2);
|
||||
unsigned long addr;
|
||||
|
||||
prandom_bytes(&addr, sizeof(addr));
|
||||
if (WARN_ON(!check_encodable(addr, size)))
|
||||
return false;
|
||||
|
||||
/* Encode and decode */
|
||||
{
|
||||
const long encoded_watchpoint =
|
||||
encode_watchpoint(addr, size, is_write);
|
||||
unsigned long verif_masked_addr;
|
||||
size_t verif_size;
|
||||
bool verif_is_write;
|
||||
|
||||
/* Check special watchpoints */
|
||||
if (WARN_ON(decode_watchpoint(
|
||||
INVALID_WATCHPOINT, &verif_masked_addr,
|
||||
&verif_size, &verif_is_write)))
|
||||
return false;
|
||||
if (WARN_ON(decode_watchpoint(
|
||||
CONSUMED_WATCHPOINT, &verif_masked_addr,
|
||||
&verif_size, &verif_is_write)))
|
||||
return false;
|
||||
|
||||
/* Check decoding watchpoint returns same data */
|
||||
if (WARN_ON(!decode_watchpoint(
|
||||
encoded_watchpoint, &verif_masked_addr,
|
||||
&verif_size, &verif_is_write)))
|
||||
return false;
|
||||
if (WARN_ON(verif_masked_addr !=
|
||||
(addr & WATCHPOINT_ADDR_MASK)))
|
||||
goto fail;
|
||||
if (WARN_ON(verif_size != size))
|
||||
goto fail;
|
||||
if (WARN_ON(is_write != verif_is_write))
|
||||
goto fail;
|
||||
|
||||
continue;
|
||||
fail:
|
||||
pr_err("%s fail: %s %zu bytes @ %lx -> encoded: %lx -> %s %zu bytes @ %lx\n",
|
||||
__func__, is_write ? "write" : "read", size,
|
||||
addr, encoded_watchpoint,
|
||||
verif_is_write ? "write" : "read", verif_size,
|
||||
verif_masked_addr);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/* Test access matching function. */
|
||||
static bool test_matching_access(void)
|
||||
{
|
||||
if (WARN_ON(!matching_access(10, 1, 10, 1)))
|
||||
return false;
|
||||
if (WARN_ON(!matching_access(10, 2, 11, 1)))
|
||||
return false;
|
||||
if (WARN_ON(!matching_access(10, 1, 9, 2)))
|
||||
return false;
|
||||
if (WARN_ON(matching_access(10, 1, 11, 1)))
|
||||
return false;
|
||||
if (WARN_ON(matching_access(9, 1, 10, 1)))
|
||||
return false;
|
||||
|
||||
/*
|
||||
* An access of size 0 could match another access, as demonstrated here.
|
||||
* Rather than add more comparisons to 'matching_access()', which would
|
||||
* end up in the fast-path for *all* checks, check_access() simply
|
||||
* returns for all accesses of size 0.
|
||||
*/
|
||||
if (WARN_ON(!matching_access(8, 8, 12, 0)))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static int __init kcsan_selftest(void)
|
||||
{
|
||||
int passed = 0;
|
||||
int total = 0;
|
||||
|
||||
#define RUN_TEST(do_test) \
|
||||
do { \
|
||||
++total; \
|
||||
if (do_test()) \
|
||||
++passed; \
|
||||
else \
|
||||
pr_err("KCSAN selftest: " #do_test " failed"); \
|
||||
} while (0)
|
||||
|
||||
RUN_TEST(test_requires);
|
||||
RUN_TEST(test_encode_decode);
|
||||
RUN_TEST(test_matching_access);
|
||||
|
||||
pr_info("KCSAN selftest: %d/%d tests passed\n", passed, total);
|
||||
if (passed != total)
|
||||
panic("KCSAN selftests failed");
|
||||
return 0;
|
||||
}
|
||||
postcore_initcall(kcsan_selftest);
|
|
@ -5,6 +5,9 @@ KCOV_INSTRUMENT := n
|
|||
|
||||
obj-y += mutex.o semaphore.o rwsem.o percpu-rwsem.o
|
||||
|
||||
# Avoid recursion lockdep -> KCSAN -> ... -> lockdep.
|
||||
KCSAN_SANITIZE_lockdep.o := n
|
||||
|
||||
ifdef CONFIG_FUNCTION_TRACER
|
||||
CFLAGS_REMOVE_lockdep.o = $(CC_FLAGS_FTRACE)
|
||||
CFLAGS_REMOVE_lockdep_proc.o = $(CC_FLAGS_FTRACE)
|
||||
|
|
|
@ -7,6 +7,12 @@ endif
|
|||
# that is not a function of syscall inputs. E.g. involuntary context switches.
|
||||
KCOV_INSTRUMENT := n
|
||||
|
||||
# There are numerous data races here, however, most of them are due to plain accesses.
|
||||
# This would make it even harder for syzbot to find reproducers, because these
|
||||
# bugs trigger without specific input. Disable by default, but should re-enable
|
||||
# eventually.
|
||||
KCSAN_SANITIZE := n
|
||||
|
||||
ifneq ($(CONFIG_SCHED_OMIT_FRAME_POINTER),y)
|
||||
# According to Alan Modra <alan@linuxcare.com.au>, the -fno-omit-frame-pointer is
|
||||
# needed for x86 only. Why this used to be enabled for all architectures is beyond
|
||||
|
|
|
@ -6,6 +6,9 @@ ifdef CONFIG_FUNCTION_TRACER
|
|||
ORIG_CFLAGS := $(KBUILD_CFLAGS)
|
||||
KBUILD_CFLAGS = $(subst $(CC_FLAGS_FTRACE),,$(ORIG_CFLAGS))
|
||||
|
||||
# Avoid recursion due to instrumentation.
|
||||
KCSAN_SANITIZE := n
|
||||
|
||||
ifdef CONFIG_FTRACE_SELFTEST
|
||||
# selftest needs instrumentation
|
||||
CFLAGS_trace_selftest_dynamic.o = $(CC_FLAGS_FTRACE)
|
||||
|
|
|
@ -1570,6 +1570,8 @@ config PROVIDE_OHCI1394_DMA_INIT
|
|||
|
||||
source "samples/Kconfig"
|
||||
|
||||
source "lib/Kconfig.kcsan"
|
||||
|
||||
config ARCH_HAS_DEVMEM_IS_ALLOWED
|
||||
bool
|
||||
|
||||
|
|
|
@ -0,0 +1,199 @@
|
|||
# SPDX-License-Identifier: GPL-2.0-only
|
||||
|
||||
config HAVE_ARCH_KCSAN
|
||||
bool
|
||||
|
||||
config HAVE_KCSAN_COMPILER
|
||||
def_bool CC_IS_CLANG && $(cc-option,-fsanitize=thread -mllvm -tsan-distinguish-volatile=1)
|
||||
help
|
||||
For the list of compilers that support KCSAN, please see
|
||||
<file:Documentation/dev-tools/kcsan.rst>.
|
||||
|
||||
config KCSAN_KCOV_BROKEN
|
||||
def_bool KCOV && CC_HAS_SANCOV_TRACE_PC
|
||||
depends on CC_IS_CLANG
|
||||
depends on !$(cc-option,-Werror=unused-command-line-argument -fsanitize=thread -fsanitize-coverage=trace-pc)
|
||||
help
|
||||
Some versions of clang support either KCSAN and KCOV but not the
|
||||
combination of the two.
|
||||
See https://bugs.llvm.org/show_bug.cgi?id=45831 for the status
|
||||
in newer releases.
|
||||
|
||||
menuconfig KCSAN
|
||||
bool "KCSAN: dynamic data race detector"
|
||||
depends on HAVE_ARCH_KCSAN && HAVE_KCSAN_COMPILER
|
||||
depends on DEBUG_KERNEL && !KASAN
|
||||
depends on !KCSAN_KCOV_BROKEN
|
||||
select STACKTRACE
|
||||
help
|
||||
The Kernel Concurrency Sanitizer (KCSAN) is a dynamic
|
||||
data-race detector that relies on compile-time instrumentation.
|
||||
KCSAN uses a watchpoint-based sampling approach to detect races.
|
||||
|
||||
While KCSAN's primary purpose is to detect data races, it
|
||||
also provides assertions to check data access constraints.
|
||||
These assertions can expose bugs that do not manifest as
|
||||
data races.
|
||||
|
||||
See <file:Documentation/dev-tools/kcsan.rst> for more details.
|
||||
|
||||
if KCSAN
|
||||
|
||||
config KCSAN_VERBOSE
|
||||
bool "Show verbose reports with more information about system state"
|
||||
depends on PROVE_LOCKING
|
||||
help
|
||||
If enabled, reports show more information about the system state that
|
||||
may help better analyze and debug races. This includes held locks and
|
||||
IRQ trace events.
|
||||
|
||||
While this option should generally be benign, we call into more
|
||||
external functions on report generation; if a race report is
|
||||
generated from any one of them, system stability may suffer due to
|
||||
deadlocks or recursion. If in doubt, say N.
|
||||
|
||||
config KCSAN_DEBUG
|
||||
bool "Debugging of KCSAN internals"
|
||||
|
||||
config KCSAN_SELFTEST
|
||||
bool "Perform short selftests on boot"
|
||||
default y
|
||||
help
|
||||
Run KCSAN selftests on boot. On test failure, causes the kernel to panic.
|
||||
|
||||
config KCSAN_EARLY_ENABLE
|
||||
bool "Early enable during boot"
|
||||
default y
|
||||
help
|
||||
If KCSAN should be enabled globally as soon as possible. KCSAN can
|
||||
later be enabled/disabled via debugfs.
|
||||
|
||||
config KCSAN_NUM_WATCHPOINTS
|
||||
int "Number of available watchpoints"
|
||||
default 64
|
||||
help
|
||||
Total number of available watchpoints. An address range maps into a
|
||||
specific watchpoint slot as specified in kernel/kcsan/encoding.h.
|
||||
Although larger number of watchpoints may not be usable due to
|
||||
limited number of CPUs, a larger value helps to improve performance
|
||||
due to reducing cache-line contention. The chosen default is a
|
||||
conservative value; we should almost never observe "no_capacity"
|
||||
events (see /sys/kernel/debug/kcsan).
|
||||
|
||||
config KCSAN_UDELAY_TASK
|
||||
int "Delay in microseconds (for tasks)"
|
||||
default 80
|
||||
help
|
||||
For tasks, the microsecond delay after setting up a watchpoint.
|
||||
|
||||
config KCSAN_UDELAY_INTERRUPT
|
||||
int "Delay in microseconds (for interrupts)"
|
||||
default 20
|
||||
help
|
||||
For interrupts, the microsecond delay after setting up a watchpoint.
|
||||
Interrupts have tighter latency requirements, and their delay should
|
||||
be lower than for tasks.
|
||||
|
||||
config KCSAN_DELAY_RANDOMIZE
|
||||
bool "Randomize above delays"
|
||||
default y
|
||||
help
|
||||
If delays should be randomized, where the maximum is KCSAN_UDELAY_*.
|
||||
If false, the chosen delays are always the KCSAN_UDELAY_* values
|
||||
as defined above.
|
||||
|
||||
config KCSAN_SKIP_WATCH
|
||||
int "Skip instructions before setting up watchpoint"
|
||||
default 4000
|
||||
help
|
||||
The number of per-CPU memory operations to skip, before another
|
||||
watchpoint is set up, i.e. one in KCSAN_WATCH_SKIP per-CPU
|
||||
memory operations are used to set up a watchpoint. A smaller value
|
||||
results in more aggressive race detection, whereas a larger value
|
||||
improves system performance at the cost of missing some races.
|
||||
|
||||
config KCSAN_SKIP_WATCH_RANDOMIZE
|
||||
bool "Randomize watchpoint instruction skip count"
|
||||
default y
|
||||
help
|
||||
If instruction skip count should be randomized, where the maximum is
|
||||
KCSAN_WATCH_SKIP. If false, the chosen value is always
|
||||
KCSAN_WATCH_SKIP.
|
||||
|
||||
config KCSAN_INTERRUPT_WATCHER
|
||||
bool "Interruptible watchers"
|
||||
help
|
||||
If enabled, a task that set up a watchpoint may be interrupted while
|
||||
delayed. This option will allow KCSAN to detect races between
|
||||
interrupted tasks and other threads of execution on the same CPU.
|
||||
|
||||
Currently disabled by default, because not all safe per-CPU access
|
||||
primitives and patterns may be accounted for, and therefore could
|
||||
result in false positives.
|
||||
|
||||
config KCSAN_REPORT_ONCE_IN_MS
|
||||
int "Duration in milliseconds, in which any given race is only reported once"
|
||||
default 3000
|
||||
help
|
||||
Any given race is only reported once in the defined time window.
|
||||
Different races may still generate reports within a duration that is
|
||||
smaller than the duration defined here. This allows rate limiting
|
||||
reporting to avoid flooding the console with reports. Setting this
|
||||
to 0 disables rate limiting.
|
||||
|
||||
# The main purpose of the below options is to control reported data races (e.g.
|
||||
# in fuzzer configs), and are not expected to be switched frequently by other
|
||||
# users. We could turn some of them into boot parameters, but given they should
|
||||
# not be switched normally, let's keep them here to simplify configuration.
|
||||
#
|
||||
# The defaults below are chosen to be very conservative, and may miss certain
|
||||
# bugs.
|
||||
|
||||
config KCSAN_REPORT_RACE_UNKNOWN_ORIGIN
|
||||
bool "Report races of unknown origin"
|
||||
default y
|
||||
help
|
||||
If KCSAN should report races where only one access is known, and the
|
||||
conflicting access is of unknown origin. This type of race is
|
||||
reported if it was only possible to infer a race due to a data value
|
||||
change while an access is being delayed on a watchpoint.
|
||||
|
||||
config KCSAN_REPORT_VALUE_CHANGE_ONLY
|
||||
bool "Only report races where watcher observed a data value change"
|
||||
default y
|
||||
help
|
||||
If enabled and a conflicting write is observed via a watchpoint, but
|
||||
the data value of the memory location was observed to remain
|
||||
unchanged, do not report the data race.
|
||||
|
||||
config KCSAN_ASSUME_PLAIN_WRITES_ATOMIC
|
||||
bool "Assume that plain aligned writes up to word size are atomic"
|
||||
default y
|
||||
help
|
||||
Assume that plain aligned writes up to word size are atomic by
|
||||
default, and also not subject to other unsafe compiler optimizations
|
||||
resulting in data races. This will cause KCSAN to not report data
|
||||
races due to conflicts where the only plain accesses are aligned
|
||||
writes up to word size: conflicts between marked reads and plain
|
||||
aligned writes up to word size will not be reported as data races;
|
||||
notice that data races between two conflicting plain aligned writes
|
||||
will also not be reported.
|
||||
|
||||
config KCSAN_IGNORE_ATOMICS
|
||||
bool "Do not instrument marked atomic accesses"
|
||||
help
|
||||
Never instrument marked atomic accesses. This option can be used for
|
||||
additional filtering. Conflicting marked atomic reads and plain
|
||||
writes will never be reported as a data race, however, will cause
|
||||
plain reads and marked writes to result in "unknown origin" reports.
|
||||
If combined with CONFIG_KCSAN_REPORT_RACE_UNKNOWN_ORIGIN=n, data
|
||||
races where at least one access is marked atomic will never be
|
||||
reported.
|
||||
|
||||
Similar to KCSAN_ASSUME_PLAIN_WRITES_ATOMIC, but including unaligned
|
||||
accesses, conflicting marked atomic reads and plain writes will not
|
||||
be reported as data races; however, unlike that option, data races
|
||||
due to two conflicting plain writes will be reported (aligned and
|
||||
unaligned, if CONFIG_KCSAN_ASSUME_PLAIN_WRITES_ATOMIC=n).
|
||||
|
||||
endif # KCSAN
|
|
@ -26,9 +26,20 @@ config UBSAN_TRAP
|
|||
the system. For some system builders this is an acceptable
|
||||
trade-off.
|
||||
|
||||
config UBSAN_KCOV_BROKEN
|
||||
def_bool KCOV && CC_HAS_SANCOV_TRACE_PC
|
||||
depends on CC_IS_CLANG
|
||||
depends on !$(cc-option,-Werror=unused-command-line-argument -fsanitize=bounds -fsanitize-coverage=trace-pc)
|
||||
help
|
||||
Some versions of clang support either UBSAN or KCOV but not the
|
||||
combination of the two.
|
||||
See https://bugs.llvm.org/show_bug.cgi?id=45831 for the status
|
||||
in newer releases.
|
||||
|
||||
config UBSAN_BOUNDS
|
||||
bool "Perform array index bounds checking"
|
||||
default UBSAN
|
||||
depends on !UBSAN_KCOV_BROKEN
|
||||
help
|
||||
This option enables detection of directly indexed out of bounds
|
||||
array accesses, where the array size is known at compile time.
|
||||
|
|
|
@ -25,6 +25,9 @@ KASAN_SANITIZE_string.o := n
|
|||
CFLAGS_string.o := $(call cc-option, -fno-stack-protector)
|
||||
endif
|
||||
|
||||
# Used by KCSAN while enabled, avoid recursion.
|
||||
KCSAN_SANITIZE_random32.o := n
|
||||
|
||||
lib-y := ctype.o string.o vsprintf.o cmdline.o \
|
||||
rbtree.o radix-tree.o timerqueue.o xarray.o \
|
||||
idr.o extable.o sha1.o irq_regs.o argv_split.o \
|
||||
|
@ -296,6 +299,7 @@ endif
|
|||
|
||||
UBSAN_SANITIZE_ubsan.o := n
|
||||
KASAN_SANITIZE_ubsan.o := n
|
||||
KCSAN_SANITIZE_ubsan.o := n
|
||||
CFLAGS_ubsan.o := $(call cc-option, -fno-stack-protector) $(DISABLE_STACKLEAK_PLUGIN)
|
||||
|
||||
obj-$(CONFIG_SBITMAP) += sbitmap.o
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
#include <linux/splice.h>
|
||||
#include <net/checksum.h>
|
||||
#include <linux/scatterlist.h>
|
||||
#include <linux/instrumented.h>
|
||||
|
||||
#define PIPE_PARANOIA /* for now */
|
||||
|
||||
|
@ -138,7 +139,7 @@
|
|||
static int copyout(void __user *to, const void *from, size_t n)
|
||||
{
|
||||
if (access_ok(to, n)) {
|
||||
kasan_check_read(from, n);
|
||||
instrument_copy_to_user(to, from, n);
|
||||
n = raw_copy_to_user(to, from, n);
|
||||
}
|
||||
return n;
|
||||
|
@ -147,7 +148,7 @@ static int copyout(void __user *to, const void *from, size_t n)
|
|||
static int copyin(void *to, const void __user *from, size_t n)
|
||||
{
|
||||
if (access_ok(from, n)) {
|
||||
kasan_check_write(to, n);
|
||||
instrument_copy_from_user(to, from, n);
|
||||
n = raw_copy_from_user(to, from, n);
|
||||
}
|
||||
return n;
|
||||
|
@ -639,7 +640,7 @@ EXPORT_SYMBOL(_copy_to_iter);
|
|||
static int copyout_mcsafe(void __user *to, const void *from, size_t n)
|
||||
{
|
||||
if (access_ok(to, n)) {
|
||||
kasan_check_read(from, n);
|
||||
instrument_copy_to_user(to, from, n);
|
||||
n = copy_to_user_mcsafe((__force void *) to, from, n);
|
||||
}
|
||||
return n;
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
// SPDX-License-Identifier: GPL-2.0
|
||||
#include <linux/uaccess.h>
|
||||
#include <linux/bitops.h>
|
||||
#include <linux/instrumented.h>
|
||||
#include <linux/uaccess.h>
|
||||
|
||||
/* out-of-line parts */
|
||||
|
||||
|
@ -10,7 +11,7 @@ unsigned long _copy_from_user(void *to, const void __user *from, unsigned long n
|
|||
unsigned long res = n;
|
||||
might_fault();
|
||||
if (likely(access_ok(from, n))) {
|
||||
kasan_check_write(to, n);
|
||||
instrument_copy_from_user(to, from, n);
|
||||
res = raw_copy_from_user(to, from, n);
|
||||
}
|
||||
if (unlikely(res))
|
||||
|
@ -25,7 +26,7 @@ unsigned long _copy_to_user(void __user *to, const void *from, unsigned long n)
|
|||
{
|
||||
might_fault();
|
||||
if (likely(access_ok(to, n))) {
|
||||
kasan_check_read(from, n);
|
||||
instrument_copy_to_user(to, from, n);
|
||||
n = raw_copy_to_user(to, from, n);
|
||||
}
|
||||
return n;
|
||||
|
|
|
@ -8,6 +8,14 @@ KASAN_SANITIZE_slab.o := n
|
|||
KASAN_SANITIZE_slub.o := n
|
||||
KCSAN_SANITIZE_kmemleak.o := n
|
||||
|
||||
# These produce frequent data race reports: most of them are due to races on
|
||||
# the same word but accesses to different bits of that word. Re-enable KCSAN
|
||||
# for these when we have more consensus on what to do about them.
|
||||
KCSAN_SANITIZE_slab_common.o := n
|
||||
KCSAN_SANITIZE_slab.o := n
|
||||
KCSAN_SANITIZE_slub.o := n
|
||||
KCSAN_SANITIZE_page_alloc.o := n
|
||||
|
||||
# These files are disabled because they produce non-interesting and/or
|
||||
# flaky coverage that is not a function of syscall inputs. E.g. slab is out of
|
||||
# free pages, or a task is migrated between nodes.
|
||||
|
|
|
@ -0,0 +1,19 @@
|
|||
# SPDX-License-Identifier: GPL-2.0
|
||||
ifdef CONFIG_KCSAN
|
||||
|
||||
# GCC and Clang accept backend options differently. Do not wrap in cc-option,
|
||||
# because Clang accepts "--param" even if it is unused.
|
||||
ifdef CONFIG_CC_IS_CLANG
|
||||
cc-param = -mllvm -$(1)
|
||||
else
|
||||
cc-param = --param -$(1)
|
||||
endif
|
||||
|
||||
# Keep most options here optional, to allow enabling more compilers if absence
|
||||
# of some options does not break KCSAN nor causes false positive reports.
|
||||
CFLAGS_KCSAN := -fsanitize=thread \
|
||||
$(call cc-option,$(call cc-param,tsan-instrument-func-entry-exit=0) -fno-optimize-sibling-calls) \
|
||||
$(call cc-option,$(call cc-param,tsan-instrument-read-before-write=1)) \
|
||||
$(call cc-param,tsan-distinguish-volatile=1)
|
||||
|
||||
endif # CONFIG_KCSAN
|
|
@ -152,6 +152,16 @@ _c_flags += $(if $(patsubst n%,, \
|
|||
$(CFLAGS_KCOV))
|
||||
endif
|
||||
|
||||
#
|
||||
# Enable KCSAN flags except some files or directories we don't want to check
|
||||
# (depends on variables KCSAN_SANITIZE_obj.o, KCSAN_SANITIZE)
|
||||
#
|
||||
ifeq ($(CONFIG_KCSAN),y)
|
||||
_c_flags += $(if $(patsubst n%,, \
|
||||
$(KCSAN_SANITIZE_$(basetarget).o)$(KCSAN_SANITIZE)y), \
|
||||
$(CFLAGS_KCSAN))
|
||||
endif
|
||||
|
||||
# $(srctree)/$(src) for including checkin headers from generated source files
|
||||
# $(objtree)/$(obj) for including generated headers from checkin source files
|
||||
ifeq ($(KBUILD_EXTMOD),)
|
||||
|
|
|
@ -20,7 +20,7 @@ gen_param_check()
|
|||
# We don't write to constant parameters
|
||||
[ ${type#c} != ${type} ] && rw="read"
|
||||
|
||||
printf "\tkasan_check_${rw}(${name}, sizeof(*${name}));\n"
|
||||
printf "\tinstrument_atomic_${rw}(${name}, sizeof(*${name}));\n"
|
||||
}
|
||||
|
||||
#gen_param_check(arg...)
|
||||
|
@ -84,7 +84,7 @@ gen_proto_order_variant()
|
|||
[ ! -z "${guard}" ] && printf "#if ${guard}\n"
|
||||
|
||||
cat <<EOF
|
||||
static inline ${ret}
|
||||
static __always_inline ${ret}
|
||||
${atomicname}(${params})
|
||||
{
|
||||
${checks}
|
||||
|
@ -107,7 +107,7 @@ cat <<EOF
|
|||
#define ${xchg}(ptr, ...) \\
|
||||
({ \\
|
||||
typeof(ptr) __ai_ptr = (ptr); \\
|
||||
kasan_check_write(__ai_ptr, ${mult}sizeof(*__ai_ptr)); \\
|
||||
instrument_atomic_write(__ai_ptr, ${mult}sizeof(*__ai_ptr)); \\
|
||||
arch_${xchg}(__ai_ptr, __VA_ARGS__); \\
|
||||
})
|
||||
EOF
|
||||
|
@ -147,7 +147,8 @@ cat << EOF
|
|||
#define _ASM_GENERIC_ATOMIC_INSTRUMENTED_H
|
||||
|
||||
#include <linux/build_bug.h>
|
||||
#include <linux/kasan-checks.h>
|
||||
#include <linux/compiler.h>
|
||||
#include <linux/instrumented.h>
|
||||
|
||||
EOF
|
||||
|
||||
|
|
|
@ -46,7 +46,7 @@ gen_proto_order_variant()
|
|||
local retstmt="$(gen_ret_stmt "${meta}")"
|
||||
|
||||
cat <<EOF
|
||||
static inline ${ret}
|
||||
static __always_inline ${ret}
|
||||
atomic_long_${name}(${params})
|
||||
{
|
||||
${retstmt}${atomic}_${name}(${argscast});
|
||||
|
@ -64,6 +64,7 @@ cat << EOF
|
|||
#ifndef _ASM_GENERIC_ATOMIC_LONG_H
|
||||
#define _ASM_GENERIC_ATOMIC_LONG_H
|
||||
|
||||
#include <linux/compiler.h>
|
||||
#include <asm/types.h>
|
||||
|
||||
#ifdef CONFIG_64BIT
|
||||
|
|
|
@ -5945,6 +5945,14 @@ sub process {
|
|||
}
|
||||
}
|
||||
|
||||
# check for data_race without a comment.
|
||||
if ($line =~ /\bdata_race\s*\(/) {
|
||||
if (!ctx_has_comment($first_line, $linenr)) {
|
||||
WARN("DATA_RACE",
|
||||
"data_race without comment\n" . $herecurr);
|
||||
}
|
||||
}
|
||||
|
||||
# check for smp_read_barrier_depends and read_barrier_depends
|
||||
if (!$file && $line =~ /\b(smp_|)read_barrier_depends\s*\(/) {
|
||||
WARN("READ_BARRIER_DEPENDS",
|
||||
|
|
|
@ -505,6 +505,28 @@ static const char *uaccess_safe_builtin[] = {
|
|||
"__asan_report_store4_noabort",
|
||||
"__asan_report_store8_noabort",
|
||||
"__asan_report_store16_noabort",
|
||||
/* KCSAN */
|
||||
"__kcsan_check_access",
|
||||
"kcsan_found_watchpoint",
|
||||
"kcsan_setup_watchpoint",
|
||||
"kcsan_check_scoped_accesses",
|
||||
"kcsan_disable_current",
|
||||
"kcsan_enable_current_nowarn",
|
||||
/* KCSAN/TSAN */
|
||||
"__tsan_func_entry",
|
||||
"__tsan_func_exit",
|
||||
"__tsan_read_range",
|
||||
"__tsan_write_range",
|
||||
"__tsan_read1",
|
||||
"__tsan_read2",
|
||||
"__tsan_read4",
|
||||
"__tsan_read8",
|
||||
"__tsan_read16",
|
||||
"__tsan_write1",
|
||||
"__tsan_write2",
|
||||
"__tsan_write4",
|
||||
"__tsan_write8",
|
||||
"__tsan_write16",
|
||||
/* KCOV */
|
||||
"write_comp_data",
|
||||
"check_kcov_mode",
|
||||
|
|
Loading…
Reference in New Issue