From: andrey.konovalov@linux.dev
To: Andrew Morton <akpm@linux-foundation.org>
Cc: Andrey Konovalov <andreyknvl@gmail.com>,
Marco Elver <elver@google.com>,
Alexander Potapenko <glider@google.com>,
Dmitry Vyukov <dvyukov@google.com>,
Andrey Ryabinin <ryabinin.a.a@gmail.com>,
kasan-dev@googlegroups.com, Peter Collingbourne <pcc@google.com>,
Evgenii Stepanov <eugenis@google.com>,
Florian Mayer <fmayer@google.com>,
linux-mm@kvack.org, linux-kernel@vger.kernel.org,
Andrey Konovalov <andreyknvl@google.com>
Subject: [PATCH mm v3 30/34] kasan: implement stack ring for tag-based modes
Date: Mon, 5 Sep 2022 23:05:45 +0200 [thread overview]
Message-ID: <692de14b6b6a1bc817fd55e4ad92fc1f83c1ab59.1662411799.git.andreyknvl@google.com> (raw)
In-Reply-To: <cover.1662411799.git.andreyknvl@google.com>
From: Andrey Konovalov <andreyknvl@google.com>
Implement storing stack depot handles for alloc/free stack traces for
slab objects for the tag-based KASAN modes in a ring buffer.
This ring buffer is referred to as the stack ring.
On each alloc/free of a slab object, the tagged address of the object and
the current stack trace are recorded in the stack ring.
On each bug report, if the accessed address belongs to a slab object, the
stack ring is scanned for matching entries. The newest entries are used to
print the alloc/free stack traces in the report: one entry for alloc and
one for free.
The number of entries in the stack ring is fixed in this patch, but one of
the following patches adds a command-line argument to control it.
Signed-off-by: Andrey Konovalov <andreyknvl@google.com>
---
Changes v2->v3:
- Drop redundant check for concurrent overwrites of stack ring entries.
Changes v1->v2:
- Only use the atomic type for pos, use READ/WRITE_ONCE() for the rest.
- Rename KASAN_STACK_RING_ENTRIES to KASAN_STACK_RING_SIZE.
- Rename object local variable in kasan_complete_mode_report_info() to
ptr to match the name in kasan_stack_ring_entry.
- Detect stack ring entry slots that are being written to.
- Use read-write lock to disallow reading half-written stack ring entries.
- Add a comment about the stack ring being best-effort.
---
mm/kasan/kasan.h | 21 +++++++++++++
mm/kasan/report_tags.c | 71 ++++++++++++++++++++++++++++++++++++++++++
mm/kasan/tags.c | 50 +++++++++++++++++++++++++++++
3 files changed, 142 insertions(+)
diff --git a/mm/kasan/kasan.h b/mm/kasan/kasan.h
index 7df107dc400a..cfff81139d67 100644
--- a/mm/kasan/kasan.h
+++ b/mm/kasan/kasan.h
@@ -2,6 +2,7 @@
#ifndef __MM_KASAN_KASAN_H
#define __MM_KASAN_KASAN_H
+#include <linux/atomic.h>
#include <linux/kasan.h>
#include <linux/kasan-tags.h>
#include <linux/kfence.h>
@@ -233,6 +234,26 @@ struct kasan_free_meta {
#endif /* CONFIG_KASAN_GENERIC */
+#if defined(CONFIG_KASAN_SW_TAGS) || defined(CONFIG_KASAN_HW_TAGS)
+
+struct kasan_stack_ring_entry {
+ void *ptr;
+ size_t size;
+ u32 pid;
+ depot_stack_handle_t stack;
+ bool is_free;
+};
+
+#define KASAN_STACK_RING_SIZE (32 << 10)
+
+struct kasan_stack_ring {
+ rwlock_t lock;
+ atomic64_t pos;
+ struct kasan_stack_ring_entry entries[KASAN_STACK_RING_SIZE];
+};
+
+#endif /* CONFIG_KASAN_SW_TAGS || CONFIG_KASAN_HW_TAGS */
+
#if IS_ENABLED(CONFIG_KASAN_KUNIT_TEST)
/* Used in KUnit-compatible KASAN tests. */
struct kunit_kasan_status {
diff --git a/mm/kasan/report_tags.c b/mm/kasan/report_tags.c
index 5cbac2cdb177..1b78136542bb 100644
--- a/mm/kasan/report_tags.c
+++ b/mm/kasan/report_tags.c
@@ -4,8 +4,12 @@
* Copyright (c) 2020 Google, Inc.
*/
+#include <linux/atomic.h>
+
#include "kasan.h"
+extern struct kasan_stack_ring stack_ring;
+
static const char *get_bug_type(struct kasan_report_info *info)
{
/*
@@ -24,5 +28,72 @@ static const char *get_bug_type(struct kasan_report_info *info)
void kasan_complete_mode_report_info(struct kasan_report_info *info)
{
+ unsigned long flags;
+ u64 pos;
+ struct kasan_stack_ring_entry *entry;
+ void *ptr;
+ u32 pid;
+ depot_stack_handle_t stack;
+ bool is_free;
+ bool alloc_found = false, free_found = false;
+
info->bug_type = get_bug_type(info);
+
+ if (!info->cache || !info->object)
+ return;
+ }
+
+ write_lock_irqsave(&stack_ring.lock, flags);
+
+ pos = atomic64_read(&stack_ring.pos);
+
+ /*
+ * The loop below tries to find stack ring entries relevant to the
+ * buggy object. This is a best-effort process.
+ *
+ * First, another object with the same tag can be allocated in place of
+ * the buggy object. Also, since the number of entries is limited, the
+ * entries relevant to the buggy object can be overwritten.
+ */
+
+ for (u64 i = pos - 1; i != pos - 1 - KASAN_STACK_RING_SIZE; i--) {
+ if (alloc_found && free_found)
+ break;
+
+ entry = &stack_ring.entries[i % KASAN_STACK_RING_SIZE];
+
+ /* Paired with smp_store_release() in save_stack_info(). */
+ ptr = (void *)smp_load_acquire(&entry->ptr);
+
+ if (kasan_reset_tag(ptr) != info->object ||
+ get_tag(ptr) != get_tag(info->access_addr))
+ continue;
+
+ pid = READ_ONCE(entry->pid);
+ stack = READ_ONCE(entry->stack);
+ is_free = READ_ONCE(entry->is_free);
+
+ if (is_free) {
+ /*
+ * Second free of the same object.
+ * Give up on trying to find the alloc entry.
+ */
+ if (free_found)
+ break;
+
+ info->free_track.pid = pid;
+ info->free_track.stack = stack;
+ free_found = true;
+ } else {
+ /* Second alloc of the same object. Give up. */
+ if (alloc_found)
+ break;
+
+ info->alloc_track.pid = pid;
+ info->alloc_track.stack = stack;
+ alloc_found = true;
+ }
+ }
+
+ write_unlock_irqrestore(&stack_ring.lock, flags);
}
diff --git a/mm/kasan/tags.c b/mm/kasan/tags.c
index 39a0481e5228..07828021c1f5 100644
--- a/mm/kasan/tags.c
+++ b/mm/kasan/tags.c
@@ -6,6 +6,7 @@
* Copyright (c) 2020 Google, Inc.
*/
+#include <linux/atomic.h>
#include <linux/init.h>
#include <linux/kasan.h>
#include <linux/kernel.h>
@@ -16,11 +17,60 @@
#include <linux/types.h>
#include "kasan.h"
+#include "../slab.h"
+
+/* Non-zero, as initial pointer values are 0. */
+#define STACK_RING_BUSY_PTR ((void *)1)
+
+struct kasan_stack_ring stack_ring;
+
+static void save_stack_info(struct kmem_cache *cache, void *object,
+ gfp_t gfp_flags, bool is_free)
+{
+ unsigned long flags;
+ depot_stack_handle_t stack;
+ u64 pos;
+ struct kasan_stack_ring_entry *entry;
+ void *old_ptr;
+
+ stack = kasan_save_stack(gfp_flags, true);
+
+ /*
+ * Prevent save_stack_info() from modifying stack ring
+ * when kasan_complete_mode_report_info() is walking it.
+ */
+ read_lock_irqsave(&stack_ring.lock, flags);
+
+next:
+ pos = atomic64_fetch_add(1, &stack_ring.pos);
+ entry = &stack_ring.entries[pos % KASAN_STACK_RING_SIZE];
+
+ /* Detect stack ring entry slots that are being written to. */
+ old_ptr = READ_ONCE(entry->ptr);
+ if (old_ptr == STACK_RING_BUSY_PTR)
+ goto next; /* Busy slot. */
+ if (!try_cmpxchg(&entry->ptr, &old_ptr, STACK_RING_BUSY_PTR))
+ goto next; /* Busy slot. */
+
+ WRITE_ONCE(entry->size, cache->object_size);
+ WRITE_ONCE(entry->pid, current->pid);
+ WRITE_ONCE(entry->stack, stack);
+ WRITE_ONCE(entry->is_free, is_free);
+
+ /*
+ * Paired with smp_load_acquire() in kasan_complete_mode_report_info().
+ */
+ smp_store_release(&entry->ptr, (s64)object);
+
+ read_unlock_irqrestore(&stack_ring.lock, flags);
+}
void kasan_save_alloc_info(struct kmem_cache *cache, void *object, gfp_t flags)
{
+ save_stack_info(cache, object, flags, false);
}
void kasan_save_free_info(struct kmem_cache *cache, void *object)
{
+ save_stack_info(cache, object, GFP_NOWAIT, true);
}
--
2.25.1
next prev parent reply other threads:[~2022-09-05 21:11 UTC|newest]
Thread overview: 40+ messages / expand[flat|nested] mbox.gz Atom feed top
2022-09-05 21:05 [PATCH mm v3 00/34] kasan: switch tag-based modes to stack ring from per-object metadata andrey.konovalov
2022-09-05 21:05 ` [PATCH mm v3 01/34] kasan: check KASAN_NO_FREE_META in __kasan_metadata_size andrey.konovalov
2022-09-05 21:05 ` [PATCH mm v3 02/34] kasan: rename kasan_set_*_info to kasan_save_*_info andrey.konovalov
2022-09-05 21:05 ` [PATCH mm v3 03/34] kasan: move is_kmalloc check out of save_alloc_info andrey.konovalov
2022-09-05 21:05 ` [PATCH mm v3 04/34] kasan: split save_alloc_info implementations andrey.konovalov
2022-09-05 21:05 ` [PATCH mm v3 05/34] kasan: drop CONFIG_KASAN_TAGS_IDENTIFY andrey.konovalov
2022-09-05 21:05 ` [PATCH mm v3 06/34] kasan: introduce kasan_print_aux_stacks andrey.konovalov
2022-09-05 21:05 ` [PATCH mm v3 07/34] kasan: introduce kasan_get_alloc_track andrey.konovalov
2022-09-05 21:05 ` [PATCH mm v3 08/34] kasan: introduce kasan_init_object_meta andrey.konovalov
2022-09-05 21:05 ` [PATCH mm v3 09/34] kasan: clear metadata functions for tag-based modes andrey.konovalov
2022-09-05 21:05 ` [PATCH mm v3 10/34] kasan: move kasan_get_*_meta to generic.c andrey.konovalov
2022-09-05 21:05 ` [PATCH mm v3 11/34] kasan: introduce kasan_requires_meta andrey.konovalov
2022-09-05 21:05 ` [PATCH mm v3 12/34] kasan: introduce kasan_init_cache_meta andrey.konovalov
2022-09-05 21:05 ` [PATCH mm v3 13/34] kasan: drop CONFIG_KASAN_GENERIC check from kasan_init_cache_meta andrey.konovalov
2022-09-05 21:05 ` [PATCH mm v3 14/34] kasan: only define kasan_metadata_size for Generic mode andrey.konovalov
2022-09-05 21:05 ` [PATCH mm v3 15/34] kasan: only define kasan_never_merge " andrey.konovalov
2022-09-05 21:05 ` [PATCH mm v3 16/34] kasan: only define metadata offsets " andrey.konovalov
2022-09-05 21:05 ` [PATCH mm v3 17/34] kasan: only define metadata structs " andrey.konovalov
2022-09-05 21:05 ` [PATCH mm v3 18/34] kasan: only define kasan_cache_create " andrey.konovalov
2022-09-05 21:05 ` [PATCH mm v3 19/34] kasan: pass tagged pointers to kasan_save_alloc/free_info andrey.konovalov
2022-09-05 21:05 ` [PATCH mm v3 20/34] kasan: move kasan_get_alloc/free_track definitions andrey.konovalov
2022-09-05 21:05 ` [PATCH mm v3 21/34] kasan: cosmetic changes in report.c andrey.konovalov
2022-09-05 21:05 ` [PATCH mm v3 22/34] kasan: use virt_addr_valid in kasan_addr_to_page/slab andrey.konovalov
2022-09-05 21:05 ` [PATCH mm v3 23/34] kasan: use kasan_addr_to_slab in print_address_description andrey.konovalov
2022-09-05 21:05 ` [PATCH mm v3 24/34] kasan: make kasan_addr_to_page static andrey.konovalov
2022-09-05 21:05 ` [PATCH mm v3 25/34] kasan: simplify print_report andrey.konovalov
2022-09-05 21:05 ` [PATCH mm v3 26/34] kasan: introduce complete_report_info andrey.konovalov
2022-09-05 21:05 ` [PATCH mm v3 27/34] kasan: fill in cache and object in complete_report_info andrey.konovalov
2022-09-05 21:05 ` [PATCH mm v3 28/34] kasan: rework function arguments in report.c andrey.konovalov
2022-09-05 21:05 ` [PATCH mm v3 29/34] kasan: introduce kasan_complete_mode_report_info andrey.konovalov
2022-09-05 21:05 ` andrey.konovalov [this message]
2022-09-05 21:05 ` [PATCH mm v3 31/34] kasan: support kasan.stacktrace for SW_TAGS andrey.konovalov
2022-09-05 21:05 ` [PATCH mm v3 32/34] kasan: dynamically allocate stack ring entries andrey.konovalov
2022-09-05 21:05 ` [PATCH mm v3 33/34] kasan: better identify bug types for tag-based modes andrey.konovalov
2022-09-05 21:05 ` [PATCH mm v3 34/34] kasan: add another use-after-free test andrey.konovalov
2022-09-11 11:50 ` [PATCH mm v3 00/34] kasan: switch tag-based modes to stack ring from per-object metadata Andrey Konovalov
2022-09-12 9:39 ` Marco Elver
2022-09-12 20:06 ` Andrew Morton
2022-09-19 8:07 ` Yu Zhao
2022-09-20 18:59 ` Andrey Konovalov
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=692de14b6b6a1bc817fd55e4ad92fc1f83c1ab59.1662411799.git.andreyknvl@google.com \
--to=andrey.konovalov@linux.dev \
--cc=akpm@linux-foundation.org \
--cc=andreyknvl@gmail.com \
--cc=andreyknvl@google.com \
--cc=dvyukov@google.com \
--cc=elver@google.com \
--cc=eugenis@google.com \
--cc=fmayer@google.com \
--cc=glider@google.com \
--cc=kasan-dev@googlegroups.com \
--cc=linux-kernel@vger.kernel.org \
--cc=linux-mm@kvack.org \
--cc=pcc@google.com \
--cc=ryabinin.a.a@gmail.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox