Re: [PATCH v3] kasan: Don't call find_vm_area() in RT kernel

From: Waiman Long
Date: Mon Feb 17 2025 - 12:56:38 EST


On 2/17/25 11:28 AM, Andrey Konovalov wrote:
On Mon, Feb 17, 2025 at 5:21 AM Waiman Long <longman@xxxxxxxxxx> wrote:
The following bug report appeared with a test run in a RT debug kernel.

[ 3359.353842] BUG: sleeping function called from invalid context at kernel/locking/spinlock_rt.c:48
[ 3359.353848] in_atomic(): 1, irqs_disabled(): 1, non_block: 0, pid: 140605, name: kunit_try_catch
[ 3359.353853] preempt_count: 1, expected: 0
:
[ 3359.353933] Call trace:
:
[ 3359.353955] rt_spin_lock+0x70/0x140
[ 3359.353959] find_vmap_area+0x84/0x168
[ 3359.353963] find_vm_area+0x1c/0x50
[ 3359.353966] print_address_description.constprop.0+0x2a0/0x320
[ 3359.353972] print_report+0x108/0x1f8
[ 3359.353976] kasan_report+0x90/0xc8
[ 3359.353980] __asan_load1+0x60/0x70

Commit e30a0361b851 ("kasan: make report_lock a raw spinlock")
changes report_lock to a raw_spinlock_t to avoid a similar RT problem.
The print_address_description() function is called with report_lock
acquired and interrupt disabled. However, the find_vm_area() function
still needs to acquire a spinlock_t which becomes a sleeping lock in
the RT kernel. IOW, we can't call find_vm_area() in a RT kernel and
changing report_lock to a raw_spinlock_t is not enough to completely
solve this RT kernel problem.

Fix this bug report by skipping the find_vm_area() call in this case
and just print out the address as is.

For !RT kernel, follow the example set in commit 0cce06ba859a
("debugobjects,locking: Annotate debug_object_fill_pool() wait type
violation") and use DEFINE_WAIT_OVERRIDE_MAP() to avoid a spinlock_t
inside raw_spinlock_t warning.

Fixes: e30a0361b851 ("kasan: make report_lock a raw spinlock")
Signed-off-by: Waiman Long <longman@xxxxxxxxxx>
---
mm/kasan/report.c | 43 ++++++++++++++++++++++++++++++-------------
1 file changed, 30 insertions(+), 13 deletions(-)

[v3] Rename helper to print_vmalloc_info_set_page.

diff --git a/mm/kasan/report.c b/mm/kasan/report.c
index 3fe77a360f1c..7c8c2e173aa4 100644
--- a/mm/kasan/report.c
+++ b/mm/kasan/report.c
@@ -370,6 +370,34 @@ static inline bool init_task_stack_addr(const void *addr)
sizeof(init_thread_union.stack));
}

+/*
+ * RT kernel cannot call find_vm_area() in atomic context. For !RT kernel,
+ * prevent spinlock_t inside raw_spinlock_t warning by raising wait-type
+ * to WAIT_SLEEP.
Quoting your response from the other thread:

Lockdep currently issues warnings for taking spinlock_t inside
raw_spinlock_t because it is not allowed in RT. Test coverage of RT
kernels is likely less than !RT kernel and so less bug of this kind will
be caught. By making !RT doing the same check, we increase coverage.
However, we do allow override in the !RT case, but it has to be done on
a case-by-case basis.
Got it.

So let's put this exactly this explanation in the comment, otherwise
it's unclear why we need something special for the !RT case.

Sure. Will do that.


+ */
+static inline void print_vmalloc_info_set_page(void *addr, struct page **ppage)
+{
+ if (!IS_ENABLED(CONFIG_PREEMPT_RT)) {
+ static DEFINE_WAIT_OVERRIDE_MAP(vmalloc_map, LD_WAIT_SLEEP);
+ struct vm_struct *va;
+
+ lock_map_acquire_try(&vmalloc_map);
+ va = find_vm_area(addr);
+ if (va) {
+ pr_err("The buggy address belongs to the virtual mapping at\n"
+ " [%px, %px) created by:\n"
+ " %pS\n",
+ va->addr, va->addr + va->size, va->caller);
+ pr_err("\n");
+
+ *ppage = vmalloc_to_page(addr);
Looking at the code again, I actually like the Andrey Ryabinin's
suggestion from the v1 thread: add a separate function that contains
an annotated call of find_vm_area(). And keep vmalloc_to_page()
outside of it, just as done in the upstream version now.

I can make the change if it is what you want.

Cheers,
Longman