When loading a module, as long as the module has memory allocation operations, kmemleak produces a false positive report that resembles the following: unreferenced object (percpu) 0x7dfd232a1650 (size 16): comm "modprobe", pid 1301, jiffies 4294940249 hex dump (first 16 bytes on cpu 2): 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ backtrace (crc 0): kmemleak_alloc_percpu+0xb4/0xd0 pcpu_alloc_noprof+0x700/0x1098 load_module+0xd4/0x348 codetag_module_init+0x20c/0x450 codetag_load_module+0x70/0xb8 load_module+0xef8/0x1608 init_module_from_file+0xec/0x158 idempotent_init_module+0x354/0x608 __arm64_sys_finit_module+0xbc/0x150 invoke_syscall+0xd4/0x258 el0_svc_common.constprop.0+0xb4/0x240 do_el0_svc+0x48/0x68 el0_svc+0x40/0xf8 el0t_64_sync_handler+0x10c/0x138 el0t_64_sync+0x1ac/0x1b0 This is because the module can only indirectly reference alloc_tag_counters through the alloc_tag section, which misleads kmemleak. However, we don't have a kmemleak ignore interface for percpu allocations yet. So let's create one and invoke it for tag->counters. [gehao@kylinos.cn: fix build error when CONFIG_DEBUG_KMEMLEAK=n, s/igonore/ignore/] Link: https://lkml.kernel.org/r/20250620093102.2416767-1-hao.ge@linux.dev Link: https://lkml.kernel.org/r/20250619183154.2122608-1-hao.ge@linux.dev Fixes: 12ca42c23775 ("alloc_tag: allocate percpu counters for module tags dynamically") Signed-off-by: Hao Ge <gehao@kylinos.cn> Reviewed-by: Catalin Marinas <catalin.marinas@arm.com> Acked-by: Suren Baghdasaryan <surenb@google.com> [lib/alloc_tag.c] Cc: Kent Overstreet <kent.overstreet@linux.dev> Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Unified · Split
| @@ -28,6 +28,7 @@ extern void kmemleak_update_trace(const void *ptr) __ref; · 27 unmodified lines | |||
| 28 | extern void kmemleak_not_leak(const void *ptr) __ref; | 28 | extern void kmemleak_not_leak(const void *ptr) __ref; |
| 29 | extern void kmemleak_transient_leak(const void *ptr) __ref; | 29 | extern void kmemleak_transient_leak(const void *ptr) __ref; |
| 30 | extern void kmemleak_ignore(const void *ptr) __ref; | 30 | extern void kmemleak_ignore(const void *ptr) __ref; |
| 31 | extern void kmemleak_ignore_percpu(const void __percpu *ptr) __ref; | ||
| 31 | extern void kmemleak_scan_area(const void *ptr, size_t size, gfp_t gfp) __ref; | 32 | extern void kmemleak_scan_area(const void *ptr, size_t size, gfp_t gfp) __ref; |
| 32 | extern void kmemleak_no_scan(const void *ptr) __ref; | 33 | extern void kmemleak_no_scan(const void *ptr) __ref; |
| 33 | extern void kmemleak_alloc_phys(phys_addr_t phys, size_t size, | 34 | extern void kmemleak_alloc_phys(phys_addr_t phys, size_t size, |
| @@ -97,6 +98,9 @@ static inline void kmemleak_not_leak(const void *ptr) · 63 unmodified lines | |||
| 97 | static inline void kmemleak_transient_leak(const void *ptr) | 98 | static inline void kmemleak_transient_leak(const void *ptr) |
| 98 | { | 99 | { |
| 99 | } | 100 | } |
| 101 | static inline void kmemleak_ignore_percpu(const void __percpu *ptr) | ||
| 102 | { | ||
| 103 | } | ||
| 100 | static inline void kmemleak_ignore(const void *ptr) | 104 | static inline void kmemleak_ignore(const void *ptr) |
| 101 | { | 105 | { |
| 102 | } | 106 | } |
| @@ -10,6 +10,7 @@ · 9 unmodified lines | |||
| 10 | #include <linux/seq_buf.h> | 10 | #include <linux/seq_buf.h> |
| 11 | #include <linux/seq_file.h> | 11 | #include <linux/seq_file.h> |
| 12 | #include <linux/vmalloc.h> | 12 | #include <linux/vmalloc.h> |
| 13 | #include <linux/kmemleak.h> | ||
| 13 | 14 | ||
| 14 | #define ALLOCINFO_FILE_NAME "allocinfo" | 15 | #define ALLOCINFO_FILE_NAME "allocinfo" |
| 15 | #define MODULE_ALLOC_TAG_VMAP_SIZE (100000UL * sizeof(struct alloc_tag)) | 16 | #define MODULE_ALLOC_TAG_VMAP_SIZE (100000UL * sizeof(struct alloc_tag)) |
| @@ -632,8 +633,13 @@ static int load_module(struct module *mod, struct codetag *start, struct codetag · 616 unmodified lines | |||
| 632 | mod->name); | 633 | mod->name); |
| 633 | return -ENOMEM; | 634 | return -ENOMEM; |
| 634 | } | 635 | } |
| 635 | } | ||
| 636 | 636 | ||
| 637 | /* | ||
| 638 | * Avoid a kmemleak false positive. The pointer to the counters is stored | ||
| 639 | * in the alloc_tag section of the module and cannot be directly accessed. | ||
| 640 | */ | ||
| 641 | kmemleak_ignore_percpu(tag->counters); | ||
| 642 | } | ||
| 637 | return 0; | 643 | return 0; |
| 638 | } | 644 | } |
| 639 | 645 | ||
| @@ -1246,6 +1246,20 @@ void __ref kmemleak_transient_leak(const void *ptr) · 1245 unmodified lines | |||
| 1246 | } | 1246 | } |
| 1247 | EXPORT_SYMBOL(kmemleak_transient_leak); | 1247 | EXPORT_SYMBOL(kmemleak_transient_leak); |
| 1248 | 1248 | ||
| 1249 | /** | ||
| 1250 | * kmemleak_ignore_percpu - similar to kmemleak_ignore but taking a percpu | ||
| 1251 | * address argument | ||
| 1252 | * @ptr: percpu address of the object | ||
| 1253 | */ | ||
| 1254 | void __ref kmemleak_ignore_percpu(const void __percpu *ptr) | ||
| 1255 | { | ||
| 1256 | pr_debug("%s(0x%px)\n", __func__, ptr); | ||
| 1257 | |||
| 1258 | if (kmemleak_enabled && ptr && !IS_ERR_PCPU(ptr)) | ||
| 1259 | make_black_object((unsigned long)ptr, OBJECT_PERCPU); | ||
| 1260 | } | ||
| 1261 | EXPORT_SYMBOL_GPL(kmemleak_ignore_percpu); | ||
| 1262 | |||
| 1249 | /** | 1263 | /** |
| 1250 | * kmemleak_ignore - ignore an allocated object | 1264 | * kmemleak_ignore - ignore an allocated object |
| 1251 | * @ptr: pointer to beginning of the object | 1265 | * @ptr: pointer to beginning of the object |