Skip to content

Commit 6c65eb7

Browse files
melverpaulmckrcu
authored andcommitted
kcsan: Start stack trace with explicit location if provided
If an explicit access address is set, as is done for scoped accesses, always start the stack trace from that location. get_stack_skipnr() is changed into sanitize_stack_entries(), which if given an address, scans the stack trace for a matching function and then replaces that entry with the explicitly provided address. The previous reports for scoped accesses were all over the place, which could be quite confusing. We now always point at the start of the scope. Signed-off-by: Marco Elver <[email protected]> Signed-off-by: Paul E. McKenney <[email protected]>
1 parent f4c87db commit 6c65eb7

File tree

2 files changed

+61
-13
lines changed

2 files changed

+61
-13
lines changed

kernel/kcsan/kcsan_test.c

Lines changed: 11 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -338,7 +338,10 @@ static noinline void test_kernel_assert_bits_nochange(void)
338338
ASSERT_EXCLUSIVE_BITS(test_var, ~TEST_CHANGE_BITS);
339339
}
340340

341-
/* To check that scoped assertions do trigger anywhere in scope. */
341+
/*
342+
* Scoped assertions do trigger anywhere in scope. However, the report should
343+
* still only point at the start of the scope.
344+
*/
342345
static noinline void test_enter_scope(void)
343346
{
344347
int x = 0;
@@ -845,22 +848,22 @@ static void test_assert_exclusive_writer_scoped(struct kunit *test)
845848
{ test_kernel_write_nochange, &test_var, sizeof(test_var), KCSAN_ACCESS_WRITE },
846849
},
847850
};
848-
const struct expect_report expect_anywhere = {
851+
const struct expect_report expect_inscope = {
849852
.access = {
850853
{ test_enter_scope, &test_var, sizeof(test_var), KCSAN_ACCESS_ASSERT | KCSAN_ACCESS_SCOPED },
851854
{ test_kernel_write_nochange, &test_var, sizeof(test_var), KCSAN_ACCESS_WRITE },
852855
},
853856
};
854857
bool match_expect_start = false;
855-
bool match_expect_anywhere = false;
858+
bool match_expect_inscope = false;
856859

857860
begin_test_checks(test_kernel_assert_writer_scoped, test_kernel_write_nochange);
858861
do {
859862
match_expect_start |= report_matches(&expect_start);
860-
match_expect_anywhere |= report_matches(&expect_anywhere);
861-
} while (!end_test_checks(match_expect_start && match_expect_anywhere));
863+
match_expect_inscope |= report_matches(&expect_inscope);
864+
} while (!end_test_checks(match_expect_inscope));
862865
KUNIT_EXPECT_TRUE(test, match_expect_start);
863-
KUNIT_EXPECT_TRUE(test, match_expect_anywhere);
866+
KUNIT_EXPECT_FALSE(test, match_expect_inscope);
864867
}
865868

866869
__no_kcsan
@@ -889,9 +892,9 @@ static void test_assert_exclusive_access_scoped(struct kunit *test)
889892
do {
890893
match_expect_start |= report_matches(&expect_start1) || report_matches(&expect_start2);
891894
match_expect_inscope |= report_matches(&expect_inscope);
892-
} while (!end_test_checks(match_expect_start && match_expect_inscope));
895+
} while (!end_test_checks(match_expect_inscope));
893896
KUNIT_EXPECT_TRUE(test, match_expect_start);
894-
KUNIT_EXPECT_TRUE(test, match_expect_inscope);
897+
KUNIT_EXPECT_FALSE(test, match_expect_inscope);
895898
}
896899

897900
/*

kernel/kcsan/report.c

Lines changed: 50 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
#include <linux/debug_locks.h>
99
#include <linux/delay.h>
1010
#include <linux/jiffies.h>
11+
#include <linux/kallsyms.h>
1112
#include <linux/kernel.h>
1213
#include <linux/lockdep.h>
1314
#include <linux/preempt.h>
@@ -301,6 +302,48 @@ static int get_stack_skipnr(const unsigned long stack_entries[], int num_entries
301302
return skip;
302303
}
303304

305+
/*
306+
* Skips to the first entry that matches the function of @ip, and then replaces
307+
* that entry with @ip, returning the entries to skip.
308+
*/
309+
static int
310+
replace_stack_entry(unsigned long stack_entries[], int num_entries, unsigned long ip)
311+
{
312+
unsigned long symbolsize, offset;
313+
unsigned long target_func;
314+
int skip;
315+
316+
if (kallsyms_lookup_size_offset(ip, &symbolsize, &offset))
317+
target_func = ip - offset;
318+
else
319+
goto fallback;
320+
321+
for (skip = 0; skip < num_entries; ++skip) {
322+
unsigned long func = stack_entries[skip];
323+
324+
if (!kallsyms_lookup_size_offset(func, &symbolsize, &offset))
325+
goto fallback;
326+
func -= offset;
327+
328+
if (func == target_func) {
329+
stack_entries[skip] = ip;
330+
return skip;
331+
}
332+
}
333+
334+
fallback:
335+
/* Should not happen; the resulting stack trace is likely misleading. */
336+
WARN_ONCE(1, "Cannot find frame for %pS in stack trace", (void *)ip);
337+
return get_stack_skipnr(stack_entries, num_entries);
338+
}
339+
340+
static int
341+
sanitize_stack_entries(unsigned long stack_entries[], int num_entries, unsigned long ip)
342+
{
343+
return ip ? replace_stack_entry(stack_entries, num_entries, ip) :
344+
get_stack_skipnr(stack_entries, num_entries);
345+
}
346+
304347
/* Compares symbolized strings of addr1 and addr2. */
305348
static int sym_strcmp(void *addr1, void *addr2)
306349
{
@@ -328,12 +371,12 @@ static void print_verbose_info(struct task_struct *task)
328371

329372
static void print_report(enum kcsan_value_change value_change,
330373
const struct access_info *ai,
331-
const struct other_info *other_info,
374+
struct other_info *other_info,
332375
u64 old, u64 new, u64 mask)
333376
{
334377
unsigned long stack_entries[NUM_STACK_ENTRIES] = { 0 };
335378
int num_stack_entries = stack_trace_save(stack_entries, NUM_STACK_ENTRIES, 1);
336-
int skipnr = get_stack_skipnr(stack_entries, num_stack_entries);
379+
int skipnr = sanitize_stack_entries(stack_entries, num_stack_entries, ai->ip);
337380
unsigned long this_frame = stack_entries[skipnr];
338381
unsigned long other_frame = 0;
339382
int other_skipnr = 0; /* silence uninit warnings */
@@ -345,8 +388,9 @@ static void print_report(enum kcsan_value_change value_change,
345388
return;
346389

347390
if (other_info) {
348-
other_skipnr = get_stack_skipnr(other_info->stack_entries,
349-
other_info->num_stack_entries);
391+
other_skipnr = sanitize_stack_entries(other_info->stack_entries,
392+
other_info->num_stack_entries,
393+
other_info->ai.ip);
350394
other_frame = other_info->stack_entries[other_skipnr];
351395

352396
/* @value_change is only known for the other thread */
@@ -585,7 +629,8 @@ static struct access_info prepare_access_info(const volatile void *ptr, size_t s
585629
.access_type = access_type,
586630
.task_pid = in_task() ? task_pid_nr(current) : -1,
587631
.cpu_id = raw_smp_processor_id(),
588-
.ip = ip,
632+
/* Only replace stack entry with @ip if scoped access. */
633+
.ip = (access_type & KCSAN_ACCESS_SCOPED) ? ip : 0,
589634
};
590635
}
591636

0 commit comments

Comments
 (0)