[PATCH 1/9] mm: kmemleak: properly disable task stack scanning

George Prekas
2023-01-23T17:05:10Z

kmemleak has a flag to enable or disable task stack scanning.
Unfortunately, this flag does not work as intended. Even when the user
disables stack scanning, kmemleak will scan them. Stacks are allocated
with vmalloc and are included in the list of objects to scan of
kmemleak.

Introduce a new function kmemleak_mark_stack that marks an allocation as
a stack. This allows kmemleak to properly decide whether to scan or not
the object based on the respective flag.

Signed-off-by: George Prekas <george@enfabrica.net>
---
include/linux/kmemleak.h | 1 +
kernel/fork.c | 3 +++
mm/kmemleak.c | 39 ++++++++++++++++++++++-----------------
3 files changed, 26 insertions(+), 17 deletions(-)

diff --git a/include/linux/kmemleak.h b/include/linux/kmemleak.h
index 6a3cd1bf4680..1e2e8deac6dc 100644
--- a/include/linux/kmemleak.h
+++ b/include/linux/kmemleak.h
@@ -33,6 +33,7 @@ extern void kmemleak_alloc_phys(phys_addr_t phys, size_t size,
gfp_t gfp) __ref;
extern void kmemleak_free_part_phys(phys_addr_t phys, size_t size) __ref;
extern void kmemleak_ignore_phys(phys_addr_t phys) __ref;
+extern void kmemleak_mark_stack(const void *ptr) __ref;

static inline void kmemleak_alloc_recursive(const void *ptr, size_t size,
int min_count, slab_flags_t flags,
diff --git a/kernel/fork.c b/kernel/fork.c
index 9f7fe3541897..e66337ce88d4 100644
--- a/kernel/fork.c
+++ b/kernel/fork.c
@@ -97,6 +97,7 @@
#include <linux/io_uring.h>
#include <linux/bpf.h>
#include <linux/stackprotector.h>
+#include <linux/kmemleak.h>

#include <asm/pgalloc.h>
#include <linux/uaccess.h>
@@ -316,6 +317,8 @@ static int alloc_thread_stack_node(struct task_struct *tsk, int node)
if (!stack)
return -ENOMEM;

+ kmemleak_mark_stack(stack);
+
vm = find_vm_area(stack);
if (memcg_charge_kernel_stack(vm)) {
vfree(stack);
diff --git a/mm/kmemleak.c b/mm/kmemleak.c
index 92f670edbf51..b40735539abd 100644
--- a/mm/kmemleak.c
+++ b/mm/kmemleak.c
@@ -176,6 +176,8 @@ struct kmemleak_object {
#define OBJECT_FULL_SCAN (1 << 3)
/* flag set for object allocated with physical address */
#define OBJECT_PHYS (1 << 4)
+/* flag set by alloc_thread_stack_node for stacks */
+#define OBJECT_STACK (1 << 5)

#define HEX_PREFIX " "
/* number of bytes to print per line; must be 16 or 32 */
@@ -1229,6 +1231,24 @@ void __ref kmemleak_ignore_phys(phys_addr_t phys)
}
EXPORT_SYMBOL(kmemleak_ignore_phys);

+/**
+ * kmemleak_mark_stack - mark the allocated object as a kernel stack
+ *
+ * @ptr: pointer to beginning of the object
+ */
+void __ref kmemleak_mark_stack(const void *ptr)
+{
+ struct kmemleak_object *object;
+
+ if (kmemleak_enabled && ptr && !IS_ERR(ptr)) {
+ object = find_and_get_object(ptr, 0);
+ if (object) {
+ object->flags |= OBJECT_STACK;
+ put_object(object);
+ }
+ }
+}
+
/*
* Update an object's checksum and return true if it was modified.
*/
@@ -1404,6 +1424,8 @@ static void scan_object(struct kmemleak_object *object)
if (!(object->flags & OBJECT_ALLOCATED))
/* already freed object */
goto out;
+ if (!kmemleak_stack_scan && object->flags & OBJECT_STACK)
+ goto out;

obj_ptr = object->flags & OBJECT_PHYS ?
__va((phys_addr_t)object->pointer) :
@@ -1586,23 +1608,6 @@ static void kmemleak_scan(void)
}
put_online_mems();

- /*
- * Scanning the task stacks (may introduce false negatives).
- */
- if (kmemleak_stack_scan) {
- struct task_struct *p, *g;
-
- rcu_read_lock();
- for_each_process_thread(g, p) {
- void *stack = try_get_task_stack(p);
- if (stack) {
- scan_block(stack, stack + THREAD_SIZE, NULL);
- put_task_stack(p);
- }
- }
- rcu_read_unlock();
- }
-
/*
* Scan the objects already referenced from the sections scanned
* above.
--
2.37.1