Commit b97efd5e authored by Linus Torvalds's avatar Linus Torvalds

Merge branch 'kcsan.2021.05.18a' of git://git.kernel.org/pub/scm/linux/kernel/git/paulmck/linux-rcu

Pull KCSAN updates from Paul McKenney.

* 'kcsan.2021.05.18a' of git://git.kernel.org/pub/scm/linux/kernel/git/paulmck/linux-rcu:
  kcsan: Use URL link for pointing access-marking.txt
  kcsan: Document "value changed" line
  kcsan: Report observed value changes
  kcsan: Remove kcsan_report_type
  kcsan: Remove reporting indirection
  kcsan: Refactor access_info initialization
  kcsan: Fold panic() call into print_report()
  kcsan: Refactor passing watchpoint/other_info
  kcsan: Distinguish kcsan_report() calls
  kcsan: Simplify value change detection
  kcsan: Add pointer to access-marking.txt to data_race() bullet
parents a412897f 117232c0
...@@ -27,75 +27,57 @@ Error reports ...@@ -27,75 +27,57 @@ Error reports
A typical data race report looks like this:: A typical data race report looks like this::
================================================================== ==================================================================
BUG: KCSAN: data-race in generic_permission / kernfs_refresh_inode BUG: KCSAN: data-race in test_kernel_read / test_kernel_write
write to 0xffff8fee4c40700c of 4 bytes by task 175 on cpu 4: write to 0xffffffffc009a628 of 8 bytes by task 487 on cpu 0:
kernfs_refresh_inode+0x70/0x170 test_kernel_write+0x1d/0x30
kernfs_iop_permission+0x4f/0x90 access_thread+0x89/0xd0
inode_permission+0x190/0x200 kthread+0x23e/0x260
link_path_walk.part.0+0x503/0x8e0 ret_from_fork+0x22/0x30
path_lookupat.isra.0+0x69/0x4d0
filename_lookup+0x136/0x280 read to 0xffffffffc009a628 of 8 bytes by task 488 on cpu 6:
user_path_at_empty+0x47/0x60 test_kernel_read+0x10/0x20
vfs_statx+0x9b/0x130 access_thread+0x89/0xd0
__do_sys_newlstat+0x50/0xb0 kthread+0x23e/0x260
__x64_sys_newlstat+0x37/0x50 ret_from_fork+0x22/0x30
do_syscall_64+0x85/0x260
entry_SYSCALL_64_after_hwframe+0x44/0xa9 value changed: 0x00000000000009a6 -> 0x00000000000009b2
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: Reported by Kernel Concurrency Sanitizer on:
CPU: 6 PID: 166 Comm: systemd-journal Not tainted 5.3.0-rc7+ #1 CPU: 6 PID: 488 Comm: access_thread Not tainted 5.12.0-rc2+ #1
Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS 1.12.0-1 04/01/2014 Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS 1.14.0-2 04/01/2014
================================================================== ==================================================================
The header of the report provides a short summary of the functions involved in 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 the race. It is followed by the access types and stack traces of the 2 threads
involved in the data race. involved in the data race. If KCSAN also observed a value change, the observed
old value and new value are shown on the "value changed" line respectively.
The other less common type of data race report looks like this:: The other less common type of data race report looks like this::
================================================================== ==================================================================
BUG: KCSAN: data-race in e1000_clean_rx_irq+0x551/0xb10 BUG: KCSAN: data-race in test_kernel_rmw_array+0x71/0xd0
race at unknown origin, with read to 0xffff933db8a2ae6c of 1 bytes by interrupt on cpu 0: race at unknown origin, with read to 0xffffffffc009bdb0 of 8 bytes by task 515 on cpu 2:
e1000_clean_rx_irq+0x551/0xb10 test_kernel_rmw_array+0x71/0xd0
e1000_clean+0x533/0xda0 access_thread+0x89/0xd0
net_rx_action+0x329/0x900 kthread+0x23e/0x260
__do_softirq+0xdb/0x2db ret_from_fork+0x22/0x30
irq_exit+0x9b/0xa0
do_IRQ+0x9c/0xf0 value changed: 0x0000000000002328 -> 0x0000000000002329
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: Reported by Kernel Concurrency Sanitizer on:
CPU: 0 PID: 0 Comm: swapper/0 Not tainted 5.3.0-rc7+ #2 CPU: 2 PID: 515 Comm: access_thread Not tainted 5.12.0-rc2+ #1
Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS 1.12.0-1 04/01/2014 Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS 1.14.0-2 04/01/2014
================================================================== ==================================================================
This report is generated where it was not possible to determine the other 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 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 memory location having changed. These reports always show a "value changed"
instrumentation or e.g. DMA accesses. These reports will only be generated if line. A common reason for reports of this type are missing instrumentation in
``CONFIG_KCSAN_REPORT_RACE_UNKNOWN_ORIGIN=y`` (selected by default). the racing thread, but could also occur due to e.g. DMA accesses. Such reports
are shown only if ``CONFIG_KCSAN_REPORT_RACE_UNKNOWN_ORIGIN=y``, which is
enabled by default.
Selective analysis Selective analysis
~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~
...@@ -106,7 +88,8 @@ the below options are available: ...@@ -106,7 +88,8 @@ the below options are available:
* KCSAN understands the ``data_race(expr)`` annotation, which tells KCSAN that * KCSAN understands the ``data_race(expr)`` annotation, which tells KCSAN that
any data races due to accesses in ``expr`` should be ignored and resulting any data races due to accesses in ``expr`` should be ignored and resulting
behaviour when encountering a data race is deemed safe. behaviour when encountering a data race is deemed safe. Please see
`"Marking Shared-Memory Accesses" in the LKMM`_ for more information.
* Disabling data race detection for entire functions can be accomplished by * Disabling data race detection for entire functions can be accomplished by
using the function attribute ``__no_kcsan``:: using the function attribute ``__no_kcsan``::
...@@ -128,6 +111,8 @@ the below options are available: ...@@ -128,6 +111,8 @@ the below options are available:
KCSAN_SANITIZE := n KCSAN_SANITIZE := n
.. _"Marking Shared-Memory Accesses" in the LKMM: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/tools/memory-model/Documentation/access-marking.txt
Furthermore, it is possible to tell KCSAN to show or hide entire classes of 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 data races, depending on preferences. These can be changed via the following
Kconfig options: Kconfig options:
......
...@@ -380,9 +380,7 @@ static noinline void kcsan_found_watchpoint(const volatile void *ptr, ...@@ -380,9 +380,7 @@ static noinline void kcsan_found_watchpoint(const volatile void *ptr,
if (consumed) { if (consumed) {
kcsan_save_irqtrace(current); kcsan_save_irqtrace(current);
kcsan_report(ptr, size, type, KCSAN_VALUE_CHANGE_MAYBE, kcsan_report_set_info(ptr, size, type, watchpoint - watchpoints);
KCSAN_REPORT_CONSUMED_WATCHPOINT,
watchpoint - watchpoints);
kcsan_restore_irqtrace(current); kcsan_restore_irqtrace(current);
} else { } else {
/* /*
...@@ -407,12 +405,7 @@ kcsan_setup_watchpoint(const volatile void *ptr, size_t size, int type) ...@@ -407,12 +405,7 @@ kcsan_setup_watchpoint(const volatile void *ptr, size_t size, int type)
const bool is_write = (type & KCSAN_ACCESS_WRITE) != 0; const bool is_write = (type & KCSAN_ACCESS_WRITE) != 0;
const bool is_assert = (type & KCSAN_ACCESS_ASSERT) != 0; const bool is_assert = (type & KCSAN_ACCESS_ASSERT) != 0;
atomic_long_t *watchpoint; atomic_long_t *watchpoint;
union { u64 old, new, diff;
u8 _1;
u16 _2;
u32 _4;
u64 _8;
} expect_value;
unsigned long access_mask; unsigned long access_mask;
enum kcsan_value_change value_change = KCSAN_VALUE_CHANGE_MAYBE; enum kcsan_value_change value_change = KCSAN_VALUE_CHANGE_MAYBE;
unsigned long ua_flags = user_access_save(); unsigned long ua_flags = user_access_save();
...@@ -468,19 +461,19 @@ kcsan_setup_watchpoint(const volatile void *ptr, size_t size, int type) ...@@ -468,19 +461,19 @@ kcsan_setup_watchpoint(const volatile void *ptr, size_t size, int type)
* Read the current value, to later check and infer a race if the data * 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. * was modified via a non-instrumented access, e.g. from a device.
*/ */
expect_value._8 = 0; old = 0;
switch (size) { switch (size) {
case 1: case 1:
expect_value._1 = READ_ONCE(*(const u8 *)ptr); old = READ_ONCE(*(const u8 *)ptr);
break; break;
case 2: case 2:
expect_value._2 = READ_ONCE(*(const u16 *)ptr); old = READ_ONCE(*(const u16 *)ptr);
break; break;
case 4: case 4:
expect_value._4 = READ_ONCE(*(const u32 *)ptr); old = READ_ONCE(*(const u32 *)ptr);
break; break;
case 8: case 8:
expect_value._8 = READ_ONCE(*(const u64 *)ptr); old = READ_ONCE(*(const u64 *)ptr);
break; break;
default: default:
break; /* ignore; we do not diff the values */ break; /* ignore; we do not diff the values */
...@@ -506,33 +499,30 @@ kcsan_setup_watchpoint(const volatile void *ptr, size_t size, int type) ...@@ -506,33 +499,30 @@ kcsan_setup_watchpoint(const volatile void *ptr, size_t size, int type)
* racy access. * racy access.
*/ */
access_mask = get_ctx()->access_mask; access_mask = get_ctx()->access_mask;
new = 0;
switch (size) { switch (size) {
case 1: case 1:
expect_value._1 ^= READ_ONCE(*(const u8 *)ptr); new = READ_ONCE(*(const u8 *)ptr);
if (access_mask)
expect_value._1 &= (u8)access_mask;
break; break;
case 2: case 2:
expect_value._2 ^= READ_ONCE(*(const u16 *)ptr); new = READ_ONCE(*(const u16 *)ptr);
if (access_mask)
expect_value._2 &= (u16)access_mask;
break; break;
case 4: case 4:
expect_value._4 ^= READ_ONCE(*(const u32 *)ptr); new = READ_ONCE(*(const u32 *)ptr);
if (access_mask)
expect_value._4 &= (u32)access_mask;
break; break;
case 8: case 8:
expect_value._8 ^= READ_ONCE(*(const u64 *)ptr); new = READ_ONCE(*(const u64 *)ptr);
if (access_mask)
expect_value._8 &= (u64)access_mask;
break; break;
default: default:
break; /* ignore; we do not diff the values */ break; /* ignore; we do not diff the values */
} }
diff = old ^ new;
if (access_mask)
diff &= access_mask;
/* Were we able to observe a value-change? */ /* Were we able to observe a value-change? */
if (expect_value._8 != 0) if (diff != 0)
value_change = KCSAN_VALUE_CHANGE_TRUE; value_change = KCSAN_VALUE_CHANGE_TRUE;
/* Check if this access raced with another. */ /* Check if this access raced with another. */
...@@ -566,8 +556,9 @@ kcsan_setup_watchpoint(const volatile void *ptr, size_t size, int type) ...@@ -566,8 +556,9 @@ kcsan_setup_watchpoint(const volatile void *ptr, size_t size, int type)
if (is_assert && value_change == KCSAN_VALUE_CHANGE_TRUE) if (is_assert && value_change == KCSAN_VALUE_CHANGE_TRUE)
atomic_long_inc(&kcsan_counters[KCSAN_COUNTER_ASSERT_FAILURES]); atomic_long_inc(&kcsan_counters[KCSAN_COUNTER_ASSERT_FAILURES]);
kcsan_report(ptr, size, type, value_change, KCSAN_REPORT_RACE_SIGNAL, kcsan_report_known_origin(ptr, size, type, value_change,
watchpoint - watchpoints); watchpoint - watchpoints,
old, new, access_mask);
} else if (value_change == KCSAN_VALUE_CHANGE_TRUE) { } else if (value_change == KCSAN_VALUE_CHANGE_TRUE) {
/* Inferring a race, since the value should not have changed. */ /* Inferring a race, since the value should not have changed. */
...@@ -576,9 +567,7 @@ kcsan_setup_watchpoint(const volatile void *ptr, size_t size, int type) ...@@ -576,9 +567,7 @@ kcsan_setup_watchpoint(const volatile void *ptr, size_t size, int type)
atomic_long_inc(&kcsan_counters[KCSAN_COUNTER_ASSERT_FAILURES]); atomic_long_inc(&kcsan_counters[KCSAN_COUNTER_ASSERT_FAILURES]);
if (IS_ENABLED(CONFIG_KCSAN_REPORT_RACE_UNKNOWN_ORIGIN) || is_assert) if (IS_ENABLED(CONFIG_KCSAN_REPORT_RACE_UNKNOWN_ORIGIN) || is_assert)
kcsan_report(ptr, size, type, KCSAN_VALUE_CHANGE_TRUE, kcsan_report_unknown_origin(ptr, size, type, old, new, access_mask);
KCSAN_REPORT_RACE_UNKNOWN_ORIGIN,
watchpoint - watchpoints);
} }
/* /*
......
...@@ -116,30 +116,27 @@ enum kcsan_value_change { ...@@ -116,30 +116,27 @@ enum kcsan_value_change {
KCSAN_VALUE_CHANGE_TRUE, KCSAN_VALUE_CHANGE_TRUE,
}; };
enum kcsan_report_type { /*
/* * The calling thread hit and consumed a watchpoint: set the access information
* The thread that set up the watchpoint and briefly stalled was * to be consumed by the reporting thread. No report is printed yet.
* signalled that another thread triggered the watchpoint. */
*/ void kcsan_report_set_info(const volatile void *ptr, size_t size, int access_type,
KCSAN_REPORT_RACE_SIGNAL, int watchpoint_idx);
/*
* 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 * The calling thread observed that the watchpoint it set up was hit and
* value before and after the stall differs. * consumed: print the full report based on information set by the racing
*/ * thread.
KCSAN_REPORT_RACE_UNKNOWN_ORIGIN, */
}; void kcsan_report_known_origin(const volatile void *ptr, size_t size, int access_type,
enum kcsan_value_change value_change, int watchpoint_idx,
u64 old, u64 new, u64 mask);
/* /*
* Print a race report from thread that encountered the race. * No other thread was observed to race with the access, but the data value
* before and after the stall differs. Reports a race of "unknown origin".
*/ */
extern void kcsan_report(const volatile void *ptr, size_t size, int access_type, void kcsan_report_unknown_origin(const volatile void *ptr, size_t size, int access_type,
enum kcsan_value_change value_change, u64 old, u64 new, u64 mask);
enum kcsan_report_type type, int watchpoint_idx);
#endif /* _KERNEL_KCSAN_KCSAN_H */ #endif /* _KERNEL_KCSAN_KCSAN_H */
...@@ -325,13 +325,10 @@ static void print_verbose_info(struct task_struct *task) ...@@ -325,13 +325,10 @@ static void print_verbose_info(struct task_struct *task)
print_irqtrace_events(task); print_irqtrace_events(task);
} }
/* static void print_report(enum kcsan_value_change value_change,
* 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 access_info *ai,
const struct other_info *other_info) const struct other_info *other_info,
u64 old, u64 new, u64 mask)
{ {
unsigned long stack_entries[NUM_STACK_ENTRIES] = { 0 }; unsigned long stack_entries[NUM_STACK_ENTRIES] = { 0 };
int num_stack_entries = stack_trace_save(stack_entries, NUM_STACK_ENTRIES, 1); int num_stack_entries = stack_trace_save(stack_entries, NUM_STACK_ENTRIES, 1);
...@@ -344,25 +341,24 @@ static bool print_report(enum kcsan_value_change value_change, ...@@ -344,25 +341,24 @@ static bool print_report(enum kcsan_value_change value_change,
* Must check report filter rules before starting to print. * Must check report filter rules before starting to print.
*/ */
if (skip_report(KCSAN_VALUE_CHANGE_TRUE, stack_entries[skipnr])) if (skip_report(KCSAN_VALUE_CHANGE_TRUE, stack_entries[skipnr]))
return false; return;
if (type == KCSAN_REPORT_RACE_SIGNAL) { if (other_info) {
other_skipnr = get_stack_skipnr(other_info->stack_entries, other_skipnr = get_stack_skipnr(other_info->stack_entries,
other_info->num_stack_entries); other_info->num_stack_entries);
other_frame = other_info->stack_entries[other_skipnr]; other_frame = other_info->stack_entries[other_skipnr];
/* @value_change is only known for the other thread */ /* @value_change is only known for the other thread */
if (skip_report(value_change, other_frame)) if (skip_report(value_change, other_frame))
return false; return;
} }
if (rate_limit_report(this_frame, other_frame)) if (rate_limit_report(this_frame, other_frame))
return false; return;
/* Print report header. */ /* Print report header. */
pr_err("==================================================================\n"); pr_err("==================================================================\n");
switch (type) { if (other_info) {
case KCSAN_REPORT_RACE_SIGNAL: {
int cmp; int cmp;
/* /*
...@@ -374,22 +370,15 @@ static bool print_report(enum kcsan_value_change value_change, ...@@ -374,22 +370,15 @@ static bool print_report(enum kcsan_value_change value_change,
get_bug_type(ai->access_type | other_info->ai.access_type), get_bug_type(ai->access_type | other_info->ai.access_type),
(void *)(cmp < 0 ? other_frame : this_frame), (void *)(cmp < 0 ? other_frame : this_frame),
(void *)(cmp < 0 ? this_frame : other_frame)); (void *)(cmp < 0 ? this_frame : other_frame));
} break; } else {
case KCSAN_REPORT_RACE_UNKNOWN_ORIGIN:
pr_err("BUG: KCSAN: %s in %pS\n", get_bug_type(ai->access_type), pr_err("BUG: KCSAN: %s in %pS\n", get_bug_type(ai->access_type),
(void *)this_frame); (void *)this_frame);
break;
default:
BUG();
} }
pr_err("\n"); pr_err("\n");
/* Print information about the racing accesses. */ /* Print information about the racing accesses. */
switch (type) { if (other_info) {
case KCSAN_REPORT_RACE_SIGNAL:
pr_err("%s to 0x%px of %zu bytes by %s on cpu %i:\n", 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, 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.size, get_thread_desc(other_info->ai.task_pid),
...@@ -407,16 +396,10 @@ static bool print_report(enum kcsan_value_change value_change, ...@@ -407,16 +396,10 @@ static bool print_report(enum kcsan_value_change value_change,
pr_err("%s to 0x%px of %zu bytes by %s on cpu %i:\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_access_type(ai->access_type), ai->ptr, ai->size,
get_thread_desc(ai->task_pid), ai->cpu_id); get_thread_desc(ai->task_pid), ai->cpu_id);
break; } else {
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", 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_access_type(ai->access_type), ai->ptr, ai->size,
get_thread_desc(ai->task_pid), ai->cpu_id); get_thread_desc(ai->task_pid), ai->cpu_id);
break;
default:
BUG();
} }
/* Print stack trace of this thread. */ /* Print stack trace of this thread. */
stack_trace_print(stack_entries + skipnr, num_stack_entries - skipnr, stack_trace_print(stack_entries + skipnr, num_stack_entries - skipnr,
...@@ -425,24 +408,41 @@ static bool print_report(enum kcsan_value_change value_change, ...@@ -425,24 +408,41 @@ static bool print_report(enum kcsan_value_change value_change,
if (IS_ENABLED(CONFIG_KCSAN_VERBOSE)) if (IS_ENABLED(CONFIG_KCSAN_VERBOSE))
print_verbose_info(current); print_verbose_info(current);
/* Print observed value change. */
if (ai->size <= 8) {
int hex_len = ai->size * 2;
u64 diff = old ^ new;
if (mask)
diff &= mask;
if (diff) {
pr_err("\n");
pr_err("value changed: 0x%0*llx -> 0x%0*llx\n",
hex_len, old, hex_len, new);
if (mask) {
pr_err(" bits changed: 0x%0*llx with mask 0x%0*llx\n",
hex_len, diff, hex_len, mask);
}
}
}
/* Print report footer. */ /* Print report footer. */
pr_err("\n"); pr_err("\n");
pr_err("Reported by Kernel Concurrency Sanitizer on:\n"); pr_err("Reported by Kernel Concurrency Sanitizer on:\n");
dump_stack_print_info(KERN_DEFAULT); dump_stack_print_info(KERN_DEFAULT);
pr_err("==================================================================\n"); pr_err("==================================================================\n");
return true; if (panic_on_warn)
panic("panic_on_warn set ...\n");
} }
static void release_report(unsigned long *flags, struct other_info *other_info) 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
* Use size to denote valid/invalid, since KCSAN entirely * 0-sized accesses.
* ignores 0-sized accesses. */
*/ other_info->ai.size = 0;
other_info->ai.size = 0;
raw_spin_unlock_irqrestore(&report_lock, *flags); raw_spin_unlock_irqrestore(&report_lock, *flags);
} }
...@@ -575,48 +575,42 @@ static bool prepare_report_consumer(unsigned long *flags, ...@@ -575,48 +575,42 @@ static bool prepare_report_consumer(unsigned long *flags,
return false; return false;
} }
/* static struct access_info prepare_access_info(const volatile void *ptr, size_t size,
* Depending on the report type either sets @other_info and returns false, or int access_type)
* 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) { return (struct access_info) {
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, .ptr = ptr,
.size = size, .size = size,
.access_type = access_type, .access_type = access_type,
.task_pid = in_task() ? task_pid_nr(current) : -1, .task_pid = in_task() ? task_pid_nr(current) : -1,
.cpu_id = raw_smp_processor_id() .cpu_id = raw_smp_processor_id()
}; };
struct other_info *other_info = type == KCSAN_REPORT_RACE_UNKNOWN_ORIGIN }
? NULL : &other_infos[watchpoint_idx];
void kcsan_report_set_info(const volatile void *ptr, size_t size, int access_type,
int watchpoint_idx)
{
const struct access_info ai = prepare_access_info(ptr, size, access_type);
unsigned long flags;
kcsan_disable_current(); kcsan_disable_current();
if (WARN_ON(watchpoint_idx < 0 || watchpoint_idx >= ARRAY_SIZE(other_infos))) lockdep_off(); /* See kcsan_report_known_origin(). */
goto out;
prepare_report_producer(&flags, &ai, &other_infos[watchpoint_idx]);
lockdep_on();
kcsan_enable_current();
}
void kcsan_report_known_origin(const volatile void *ptr, size_t size, int access_type,
enum kcsan_value_change value_change, int watchpoint_idx,
u64 old, u64 new, u64 mask)
{
const struct access_info ai = prepare_access_info(ptr, size, access_type);
struct other_info *other_info = &other_infos[watchpoint_idx];
unsigned long flags = 0;
kcsan_disable_current();
/* /*
* Because we may generate reports when we're in scheduler code, the use * Because we may generate reports when we're in scheduler code, the use
* of printk() could deadlock. Until such time that all printing code * of printk() could deadlock. Until such time that all printing code
...@@ -626,22 +620,35 @@ void kcsan_report(const volatile void *ptr, size_t size, int access_type, ...@@ -626,22 +620,35 @@ void kcsan_report(const volatile void *ptr, size_t size, int access_type,
*/ */
lockdep_off(); lockdep_off();
if (prepare_report(&flags, type, &ai, other_info)) { if (!prepare_report_consumer(&flags, &ai, other_info))
/* goto out;
* Never report if value_change is FALSE, only if we it is /*
* either TRUE or MAYBE. In case of MAYBE, further filtering may * Never report if value_change is FALSE, only when it is
* be done once we know the full stack trace in print_report(). * 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 (value_change != KCSAN_VALUE_CHANGE_FALSE)
print_report(value_change, &ai, other_info, old, new, mask);
if (reported && panic_on_warn) release_report(&flags, other_info);
panic("panic_on_warn set ...\n"); out:
lockdep_on();
kcsan_enable_current();
}
release_report(&flags, other_info); void kcsan_report_unknown_origin(const volatile void *ptr, size_t size, int access_type,
} u64 old, u64 new, u64 mask)
{
const struct access_info ai = prepare_access_info(ptr, size, access_type);
unsigned long flags;
kcsan_disable_current();
lockdep_off(); /* See kcsan_report_known_origin(). */
raw_spin_lock_irqsave(&report_lock, flags);
print_report(KCSAN_VALUE_CHANGE_TRUE, &ai, NULL, old, new, mask);
raw_spin_unlock_irqrestore(&report_lock, flags);
lockdep_on(); lockdep_on();
out:
kcsan_enable_current(); kcsan_enable_current();
} }
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment