diff options
-rw-r--r-- | kernel/kcsan/report.c | 115 |
1 files changed, 49 insertions, 66 deletions
diff --git a/kernel/kcsan/report.c b/kernel/kcsan/report.c index d8441bed065c..ba924f110c95 100644 --- a/kernel/kcsan/report.c +++ b/kernel/kcsan/report.c @@ -434,13 +434,11 @@ static void print_report(enum kcsan_value_change value_change, 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; - + /* + * 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); } @@ -573,61 +571,6 @@ discard: 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; - } -} - -static void kcsan_report(const struct access_info *ai, enum kcsan_value_change value_change, - enum kcsan_report_type type, struct other_info *other_info) -{ - unsigned long flags = 0; - - kcsan_disable_current(); - - /* - * Because we may generate reports when we're in scheduler code, the use - * of printk() could deadlock. Until such time that all printing code - * called in print_report() is scheduler-safe, accept the risk, and just - * get our message out. As such, also disable lockdep to hide the - * warning, and avoid disabling lockdep for the rest of the kernel. - */ - 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(). - */ - if (value_change != KCSAN_VALUE_CHANGE_FALSE) - print_report(value_change, type, ai, other_info); - - release_report(&flags, other_info); - } - - lockdep_on(); - kcsan_enable_current(); -} - static struct access_info prepare_access_info(const volatile void *ptr, size_t size, int access_type) { @@ -644,22 +587,62 @@ void kcsan_report_set_info(const volatile void *ptr, size_t size, int access_typ int watchpoint_idx) { 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(). */ - kcsan_report(&ai, KCSAN_VALUE_CHANGE_MAYBE, KCSAN_REPORT_CONSUMED_WATCHPOINT, - &other_infos[watchpoint_idx]); + 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) { 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_report(&ai, value_change, KCSAN_REPORT_RACE_SIGNAL, &other_infos[watchpoint_idx]); + kcsan_disable_current(); + /* + * Because we may generate reports when we're in scheduler code, the use + * of printk() could deadlock. Until such time that all printing code + * called in print_report() is scheduler-safe, accept the risk, and just + * get our message out. As such, also disable lockdep to hide the + * warning, and avoid disabling lockdep for the rest of the kernel. + */ + lockdep_off(); + + if (!prepare_report_consumer(&flags, &ai, other_info)) + goto out; + /* + * Never report if value_change is FALSE, only when 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(). + */ + if (value_change != KCSAN_VALUE_CHANGE_FALSE) + print_report(value_change, KCSAN_REPORT_RACE_SIGNAL, &ai, other_info); + + release_report(&flags, other_info); +out: + lockdep_on(); + kcsan_enable_current(); } void kcsan_report_unknown_origin(const volatile void *ptr, size_t size, int access_type) { 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, KCSAN_REPORT_RACE_UNKNOWN_ORIGIN, &ai, NULL); + raw_spin_unlock_irqrestore(&report_lock, flags); - kcsan_report(&ai, KCSAN_VALUE_CHANGE_TRUE, KCSAN_REPORT_RACE_UNKNOWN_ORIGIN, NULL); + lockdep_on(); + kcsan_enable_current(); } |