read_file_mod_stats() traverses dup_failed_modules with
list_for_each_entry_rcu() while holding module_mutex, but does not pass
the lockdep condition. This triggers a false-positive "RCU-list traversed
in non-reader section" warning with CONFIG_PROVE_RCU_LIST=y.

The warning can be reproduced by:
  1. Racing two loads of the same module to populate dup_failed_modules:
       insmod dummy.ko &
       insmod dummy.ko &
  2. Reading the stats debugfs file:
       cat /sys/kernel/debug/modules/stats

 =============================
 WARNING: suspicious RCU usage
 7.1.0-rc5-gae12a56ba16a #1 Not tainted
 -----------------------------
 kernel/module/stats.c:385 RCU-list traversed in non-reader section!!

 other info that might help us debug this:

 rcu_scheduler_active = 2, debug_locks = 1
 1 lock held by cat/128:
  #0: ffff80008288f7a8 (module_mutex){+.+.}-{4:4}, at: 
read_file_mod_stats+0x46c/0x5ec

 stack backtrace:
 CPU: 1 UID: 0 PID: 128 Comm: cat Kdump: loaded Not tainted 
7.1.0-rc5-gae12a56ba16a #1 PREEMPT
 Hardware name: linux,dummy-virt (DT)
 Call trace:
  show_stack+0x18/0x24 (C)
  __dump_stack+0x28/0x38
  dump_stack_lvl+0x64/0x84
  dump_stack+0x18/0x24
  lockdep_rcu_suspicious+0x134/0x1d4
  read_file_mod_stats+0x554/0x5ec
  full_proxy_read+0xe0/0x1ac
  vfs_read+0xd8/0x2b0
  ksys_read+0x70/0xe4
  __arm64_sys_read+0x1c/0x28
  invoke_syscall+0x48/0xf8
  el0_svc_common+0x8c/0xd8
  do_el0_svc+0x1c/0x28
  el0_svc+0x58/0x1d8
  el0t_64_sync_handler+0x84/0x12c
  el0t_64_sync+0x198/0x19c

The traversal is protected by module_mutex, so pass
lockdep_is_held(&module_mutex) to inform the checker.

Signed-off-by: Naveen Kumar Chaudhary <[email protected]>
---
 kernel/module/stats.c | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/kernel/module/stats.c b/kernel/module/stats.c
index 3a9672f93a8e..a62961acd8e3 100644
--- a/kernel/module/stats.c
+++ b/kernel/module/stats.c
@@ -382,7 +382,8 @@ static ssize_t read_file_mod_stats(struct file *file, char 
__user *user_buf,
        mutex_lock(&module_mutex);
 
 
-       list_for_each_entry_rcu(mod_fail, &dup_failed_modules, list) {
+       list_for_each_entry_rcu(mod_fail, &dup_failed_modules, list,
+                               lockdep_is_held(&module_mutex)) {
                if (WARN_ON_ONCE(++count_failed >= MAX_FAILED_MOD_PRINT))
                        goto out_unlock;
                len += scnprintf(buf + len, size - len, "%25s\t%15lu\t%25s\n", 
mod_fail->name,
-- 
2.43.0


Reply via email to