Received: by 2002:a25:868d:0:0:0:0:0 with SMTP id z13csp2735627ybk; Tue, 12 May 2020 06:58:22 -0700 (PDT) X-Google-Smtp-Source: ABdhPJwjWk/k9etCkBGwMglON+Btt59kxxDZxID2F4vLiGUAffOVdVMlHb0Gb/NVeeEsmlSQG32F X-Received: by 2002:a17:906:2c03:: with SMTP id e3mr2213634ejh.206.1589291902265; Tue, 12 May 2020 06:58:22 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1589291902; cv=none; d=google.com; s=arc-20160816; b=uPXcmeVsljpc0tkUm3KaU1JA77J/9ORFZPpJurxbKbK5to+bHi5iMLqS4TDne6OVQI ZbCUsnIjIYXmN0NMzRbp37sp5HhaqqNNzIv9lTABYdnNHXYHc3PpeOwxu8P1lGXtz3oz wtWudCBh0sd+mBgm1SS08RWuillrXmhwv9Nj8ta8BKVQY+MyyjjsuRUgZ1UfoX07ZB3h oBYHgXhkLJiqPDjggSMcXVVTLVWNgd8zj+2qXhesk2g0Fakl9kAYI1JMM3LnXUkogTbU jE+WL1bGvqVEqiaKzgTsZaSV7GXSkRW25C7NaLShytyQYs5DKvgRtkORYCvwzvQCZDeY EFpg== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=list-id:precedence:sender:cc:to:subject:message-id:date:from :in-reply-to:references:mime-version:dkim-signature; bh=310FlHomrUwrJ0LyBuqDcU3m3XgbbIy74peHQiPw95g=; b=CNwKPHhQTbR7IVAWXofwlegxyRCuAJgxnjktbVRWRi0byn9EiF9FgvAUIVgRupAZHK OVsZLTNypAyC7MWg4EqkvKGyGao6pKCB7g8nDmBj+w1nfQnPpu1AJDtQVPUwwzUUhYxb vC+4ls9XVi0BgiWNbbV/KQ6RlOkS3M5WGlPB7rCSRfdWbFlLvhLMWy1IZBdq5hy1wAYi sQgT4GnW68CUgXNhp7LeHVWK1FZDyiAvI/J0Oc57c5NpL+dZULlKipKWVk52w5DN/fks yzO/MUTROJGIdeGQhXLNquwD9NAN2z0145QWBhz1Q7hyx60TDe5KME8Ps8tgTeUSk8gV vImw== ARC-Authentication-Results: i=1; mx.google.com; dkim=pass header.i=@google.com header.s=20161025 header.b="kZPFHO/v"; spf=pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 23.128.96.18 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=pass (p=REJECT sp=REJECT dis=NONE) header.from=google.com Return-Path: Received: from vger.kernel.org (vger.kernel.org. [23.128.96.18]) by mx.google.com with ESMTP id u15si2201866edb.348.2020.05.12.06.57.58; Tue, 12 May 2020 06:58:22 -0700 (PDT) Received-SPF: pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 23.128.96.18 as permitted sender) client-ip=23.128.96.18; Authentication-Results: mx.google.com; dkim=pass header.i=@google.com header.s=20161025 header.b="kZPFHO/v"; spf=pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 23.128.96.18 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=pass (p=REJECT sp=REJECT dis=NONE) header.from=google.com Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1730110AbgELN4b (ORCPT + 99 others); Tue, 12 May 2020 09:56:31 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:33030 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1727859AbgELN4a (ORCPT ); Tue, 12 May 2020 09:56:30 -0400 Received: from mail-qt1-x841.google.com (mail-qt1-x841.google.com [IPv6:2607:f8b0:4864:20::841]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 86334C061A0C for ; Tue, 12 May 2020 06:56:30 -0700 (PDT) Received: by mail-qt1-x841.google.com with SMTP id j2so10767998qtr.12 for ; Tue, 12 May 2020 06:56:30 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20161025; h=mime-version:references:in-reply-to:from:date:message-id:subject:to :cc; bh=310FlHomrUwrJ0LyBuqDcU3m3XgbbIy74peHQiPw95g=; b=kZPFHO/v2zjyv8mONdMIdwOR+qW+nZchQ4OreJY3ZczNgI8FKWkP2H0PHnzQf60LTj YT2G3eAkhDqp0f6FF9yMFiJgiA2d+ZDv8ha5daOzu+w/2hEEVWbnHjbfMTWHYoqrnBqI YFAPWN4bCIfZcm6xWO5JvuRTvy3mjLl3RAe5jSN7c3NSfYsvDeIhiJ++EDIZr9nbltYx TTDQ7PaxtItfGWMaYvtlpLEvsKtumnxOTlXXggWkvCw0JspjShM+Li3ubdjqYqrYFfYG NOu2XtjK93NN5Fz9TDoS33ylsvXQM/bqOhVXJvygO9RzWXcKaVONf5C+sxealMbrS93i Om6A== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:mime-version:references:in-reply-to:from:date :message-id:subject:to:cc; bh=310FlHomrUwrJ0LyBuqDcU3m3XgbbIy74peHQiPw95g=; b=clIHmur6tzyEdiT4NoUppGoWt8f2y5gncKrWqhRHbGQXqqAFFGv1/tuv/sTsucNvwJ Y84fAAsI+Pdnn65NFGg4rKd4tv48Hp4xIOfVPXvtJZgcIlrgGLjrVkflywiC25FnozBd AHP07B246xsylSN460XDhN0o5S2rJWqMkLW6ydVQg+5s34lEKjqAL8gRkqMbvemrtamn Y2zxGOkuUTjVEToLc55nmWNJ5da0eIqDidSTXVux0G4sZWG3zdaEh08GWvXdZHFg8RnA pN/6qcG7tlSrY8EAbUDTXdySwVypwxGXz0FldwvftPh+uJcZGjd0PdmZb+umByHuApYt CJGg== X-Gm-Message-State: AOAM532PL3cSEQynQJACtt9dNEKYm3+5JvWXpMmuG1Hm0NzdmNUfPcR9 50SuYwqOBL3rZuJdUxTiey2qUYT6OyTWkjiGFyAvRA== X-Received: by 2002:ac8:260b:: with SMTP id u11mr2421299qtu.380.1589291789390; Tue, 12 May 2020 06:56:29 -0700 (PDT) MIME-Version: 1.0 References: <20200511023111.15310-1-walter-zh.wu@mediatek.com> <20200511180527.GZ2869@paulmck-ThinkPad-P72> <1589250993.19238.22.camel@mtksdccf07> In-Reply-To: <1589250993.19238.22.camel@mtksdccf07> From: Dmitry Vyukov Date: Tue, 12 May 2020 15:56:17 +0200 Message-ID: Subject: Re: [PATCH v2 1/3] rcu/kasan: record and print call_rcu() call stack To: Walter Wu Cc: "Paul E. McKenney" , Andrey Ryabinin , Alexander Potapenko , Matthias Brugger , Josh Triplett , Mathieu Desnoyers , Lai Jiangshan , Joel Fernandes , Andrew Morton , kasan-dev , Linux-MM , LKML , Linux ARM , wsd_upstream , linux-mediatek@lists.infradead.org Content-Type: text/plain; charset="UTF-8" Sender: linux-kernel-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org On Tue, May 12, 2020 at 4:36 AM Walter Wu wrote: > > On Mon, 2020-05-11 at 11:05 -0700, Paul E. McKenney wrote: > > On Mon, May 11, 2020 at 10:31:11AM +0800, Walter Wu wrote: > > > This feature will record first and last call_rcu() call stack and > > > print two call_rcu() call stack in KASAN report. > > > > Suppose that a given rcu_head structure is passed to call_rcu(), then > > the grace period elapses, the callback is invoked, and the enclosing > > data structure is freed. But then that same region of memory is > > immediately reallocated as the same type of structure and again > > passed to call_rcu(), and that this cycle repeats several times. > > > > Would the first call stack forever be associated with the first > > call_rcu() in this series? If so, wouldn't the last two usually > > be the most useful? Or am I unclear on the use case? 2 points here: 1. With KASAN the object won't be immediately reallocated. KASAN has 'quarantine' to delay reuse of heap objects. It is assumed that the object is still in quarantine when we detect a use-after-free. In such a case we will have proper call_rcu stacks as well. It is possible that the object is not in quarantine already and was reused several times (quarantine is not infinite), but then KASAN will report non-sense stacks for allocation/free as well. So wrong call_rcu stacks are less of a problem in such cases. 2. We would like to memorize 2 last call_rcu stacks regardless, but we just don't have a good place for the index (bit which of the 2 is the one to overwrite). Probably could shove it into some existing field, but then will require atomic operations, etc. Nobody knows how well/bad it will work. I think we need to get the first version in, deploy on syzbot, accumulate some base of example reports and iterate from there. > The first call stack doesn't forever associate with first call_rcu(), > if someone object freed and reallocated, then the first call stack will > replace with new object. > > > > When call_rcu() is called, we store the call_rcu() call stack into > > > slub alloc meta-data, so that KASAN report can print rcu stack. > > > > > > It doesn't increase the cost of memory consumption. Because we don't > > > enlarge struct kasan_alloc_meta size. > > > - add two call_rcu() call stack into kasan_alloc_meta, size is 8 bytes. > > > - remove free track from kasan_alloc_meta, size is 8 bytes. > > > > > > [1]https://bugzilla.kernel.org/show_bug.cgi?id=198437 > > > [2]https://groups.google.com/forum/#!searchin/kasan-dev/better$20stack$20traces$20for$20rcu%7Csort:date/kasan-dev/KQsjT_88hDE/7rNUZprRBgAJ > > > > > > Signed-off-by: Walter Wu > > > Suggested-by: Dmitry Vyukov > > > Cc: Andrey Ryabinin > > > Cc: Dmitry Vyukov > > > Cc: Alexander Potapenko > > > Cc: Andrew Morton > > > Cc: Paul E. McKenney > > > Cc: Josh Triplett > > > Cc: Mathieu Desnoyers > > > Cc: Lai Jiangshan > > > Cc: Joel Fernandes > > > --- > > > include/linux/kasan.h | 2 ++ > > > kernel/rcu/tree.c | 3 +++ > > > lib/Kconfig.kasan | 2 ++ > > > mm/kasan/common.c | 4 ++-- > > > mm/kasan/generic.c | 29 +++++++++++++++++++++++++++++ > > > mm/kasan/kasan.h | 19 +++++++++++++++++++ > > > mm/kasan/report.c | 21 +++++++++++++++++---- > > > 7 files changed, 74 insertions(+), 6 deletions(-) > > > > > > diff --git a/include/linux/kasan.h b/include/linux/kasan.h > > > index 31314ca7c635..23b7ee00572d 100644 > > > --- a/include/linux/kasan.h > > > +++ b/include/linux/kasan.h > > > @@ -174,11 +174,13 @@ static inline size_t kasan_metadata_size(struct kmem_cache *cache) { return 0; } > > > > > > void kasan_cache_shrink(struct kmem_cache *cache); > > > void kasan_cache_shutdown(struct kmem_cache *cache); > > > +void kasan_record_aux_stack(void *ptr); > > > > > > #else /* CONFIG_KASAN_GENERIC */ > > > > > > static inline void kasan_cache_shrink(struct kmem_cache *cache) {} > > > static inline void kasan_cache_shutdown(struct kmem_cache *cache) {} > > > +static inline void kasan_record_aux_stack(void *ptr) {} > > > > > > #endif /* CONFIG_KASAN_GENERIC */ > > > > > > diff --git a/kernel/rcu/tree.c b/kernel/rcu/tree.c > > > index 06548e2ebb72..de872b6cc261 100644 > > > --- a/kernel/rcu/tree.c > > > +++ b/kernel/rcu/tree.c > > > @@ -57,6 +57,7 @@ > > > #include > > > #include > > > #include > > > +#include > > > #include "../time/tick-internal.h" > > > > > > #include "tree.h" > > > @@ -2694,6 +2695,8 @@ __call_rcu(struct rcu_head *head, rcu_callback_t func) > > > trace_rcu_callback(rcu_state.name, head, > > > rcu_segcblist_n_cbs(&rdp->cblist)); > > > > > > + kasan_record_aux_stack(head); > > > > Just for the record, at this point we have not yet queued the callback. > > We have also not yet disabled interrupts. Which might be OK, but I > > figured I should call out the possibility of moving this down a few > > lines to follow the local_irq_save(). > > > > We will intend to do it. I will sleep better if we move it up :) It qualifies a "debug check", which are generally done on entrance to the function. Or are these all debug checks up to this point? But if the callback did not leak anywhere up to this point and we will maintain it that way, then formally it is fine. > > If someone incorrectly invokes concurrently invokes call_rcu() on this > > same region of memory, possibly from an interrupt handler, we are OK > > corrupting the stack traces, right? > > > > Yes, and the wrong invoking call_rcu should be recorded. > > > But what happens if a given structure has more than one rcu_head > > structure? In that case, RCU would be just fine with it being > > concurrently passed to different call_rcu() invocations as long as the > > two invocations didn't both use the same rcu_head structure. (In that > > case, they had best not be both freeing the object, and if even one of > > them is freeing the object, coordination is necessary.) > > > > If this is a problem, one approach would be to move the > > kasan_record_aux_stack(head) call to kfree_rcu(). After all, it is > > definitely illegal to pass the same memory to a pair of kfree_rcu() > > invocations! ;-) > > > > The function of kasan_record_aux_stack(head) is simple, it is only to > record call stack by the 'head' object. I would say "corrupting" stacks on some races is fine-ish. In the end we are just storing an u32 stack id. On syzbot we generally have multiple samples of the same crash, so even if one is "corrupted" there may be others that are not corrupted. Just protecting from this looks too complex and expensive. And in the end there is not much we can do anyway. Recording all call_rcu stacks (not just kfree_rcu) is intentional. I think it may be useful to even extend to recording workqueue and timer stacks as well. > Thanks. > > > Thanx, Paul > > > > > + > > > /* Go handle any RCU core processing required. */ > > > if (IS_ENABLED(CONFIG_RCU_NOCB_CPU) && > > > unlikely(rcu_segcblist_is_offloaded(&rdp->cblist))) { > > > diff --git a/lib/Kconfig.kasan b/lib/Kconfig.kasan > > > index 81f5464ea9e1..56a89291f1cc 100644 > > > --- a/lib/Kconfig.kasan > > > +++ b/lib/Kconfig.kasan > > > @@ -58,6 +58,8 @@ config KASAN_GENERIC > > > For better error detection enable CONFIG_STACKTRACE. > > > Currently CONFIG_KASAN_GENERIC doesn't work with CONFIG_DEBUG_SLAB > > > (the resulting kernel does not boot). > > > + Currently CONFIG_KASAN_GENERIC will print first and last call_rcu() > > > + call stack. It doesn't increase the cost of memory consumption. > > > > > > config KASAN_SW_TAGS > > > bool "Software tag-based mode" > > > diff --git a/mm/kasan/common.c b/mm/kasan/common.c > > > index 2906358e42f0..8bc618289bb1 100644 > > > --- a/mm/kasan/common.c > > > +++ b/mm/kasan/common.c > > > @@ -41,7 +41,7 @@ > > > #include "kasan.h" > > > #include "../slab.h" > > > > > > -static inline depot_stack_handle_t save_stack(gfp_t flags) > > > +depot_stack_handle_t kasan_save_stack(gfp_t flags) > > > { > > > unsigned long entries[KASAN_STACK_DEPTH]; > > > unsigned int nr_entries; > > > @@ -54,7 +54,7 @@ static inline depot_stack_handle_t save_stack(gfp_t flags) > > > static inline void set_track(struct kasan_track *track, gfp_t flags) > > > { > > > track->pid = current->pid; > > > - track->stack = save_stack(flags); > > > + track->stack = kasan_save_stack(flags); > > > } > > > > > > void kasan_enable_current(void) > > > diff --git a/mm/kasan/generic.c b/mm/kasan/generic.c > > > index 56ff8885fe2e..b86880c338e2 100644 > > > --- a/mm/kasan/generic.c > > > +++ b/mm/kasan/generic.c > > > @@ -325,3 +325,32 @@ DEFINE_ASAN_SET_SHADOW(f2); > > > DEFINE_ASAN_SET_SHADOW(f3); > > > DEFINE_ASAN_SET_SHADOW(f5); > > > DEFINE_ASAN_SET_SHADOW(f8); > > > + > > > +void kasan_record_aux_stack(void *addr) > > > +{ > > > + struct page *page = kasan_addr_to_page(addr); > > > + struct kmem_cache *cache; > > > + struct kasan_alloc_meta *alloc_info; > > > + void *object; > > > + > > > + if (!(page && PageSlab(page))) > > > + return; > > > + > > > + cache = page->slab_cache; > > > + object = nearest_obj(cache, page, addr); > > > + alloc_info = get_alloc_info(cache, object); > > > + > > > + if (!alloc_info->rcu_stack[0]) > > > + /* record first call_rcu() call stack */ > > > + alloc_info->rcu_stack[0] = kasan_save_stack(GFP_NOWAIT); > > > + else > > > + /* record last call_rcu() call stack */ > > > + alloc_info->rcu_stack[1] = kasan_save_stack(GFP_NOWAIT); > > > +} > > > + > > > +struct kasan_track *kasan_get_aux_stack(struct kasan_alloc_meta *alloc_info, > > > + u8 idx) > > > +{ > > > + return container_of(&alloc_info->rcu_stack[idx], > > > + struct kasan_track, stack); > > > +} > > > diff --git a/mm/kasan/kasan.h b/mm/kasan/kasan.h > > > index e8f37199d885..1cc1fb7b0de3 100644 > > > --- a/mm/kasan/kasan.h > > > +++ b/mm/kasan/kasan.h > > > @@ -96,15 +96,28 @@ struct kasan_track { > > > depot_stack_handle_t stack; > > > }; > > > > > > +#ifdef CONFIG_KASAN_GENERIC > > > +#define SIZEOF_PTR sizeof(void *) > > > +#define KASAN_NR_RCU_CALL_STACKS 2 > > > +#else /* CONFIG_KASAN_GENERIC */ > > > #ifdef CONFIG_KASAN_SW_TAGS_IDENTIFY > > > #define KASAN_NR_FREE_STACKS 5 > > > #else > > > #define KASAN_NR_FREE_STACKS 1 > > > #endif > > > +#endif /* CONFIG_KASAN_GENERIC */ > > > > > > struct kasan_alloc_meta { > > > struct kasan_track alloc_track; > > > +#ifdef CONFIG_KASAN_GENERIC > > > + /* > > > + * call_rcu() call stack is stored into struct kasan_alloc_meta. > > > + * The free stack is stored into freed object. > > > + */ > > > + depot_stack_handle_t rcu_stack[KASAN_NR_RCU_CALL_STACKS]; > > > +#else > > > struct kasan_track free_track[KASAN_NR_FREE_STACKS]; > > > +#endif > > > #ifdef CONFIG_KASAN_SW_TAGS_IDENTIFY > > > u8 free_pointer_tag[KASAN_NR_FREE_STACKS]; > > > u8 free_track_idx; > > > @@ -159,16 +172,22 @@ void kasan_report_invalid_free(void *object, unsigned long ip); > > > > > > struct page *kasan_addr_to_page(const void *addr); > > > > > > +depot_stack_handle_t kasan_save_stack(gfp_t flags); > > > + > > > #if defined(CONFIG_KASAN_GENERIC) && \ > > > (defined(CONFIG_SLAB) || defined(CONFIG_SLUB)) > > > void quarantine_put(struct kasan_free_meta *info, struct kmem_cache *cache); > > > void quarantine_reduce(void); > > > void quarantine_remove_cache(struct kmem_cache *cache); > > > +struct kasan_track *kasan_get_aux_stack(struct kasan_alloc_meta *alloc_info, > > > + u8 idx); > > > #else > > > static inline void quarantine_put(struct kasan_free_meta *info, > > > struct kmem_cache *cache) { } > > > static inline void quarantine_reduce(void) { } > > > static inline void quarantine_remove_cache(struct kmem_cache *cache) { } > > > +static inline struct kasan_track *kasan_get_aux_stack( > > > + struct kasan_alloc_meta *alloc_info, u8 idx) { return NULL; } > > > #endif > > > > > > #ifdef CONFIG_KASAN_SW_TAGS > > > diff --git a/mm/kasan/report.c b/mm/kasan/report.c > > > index 80f23c9da6b0..f16a1a210815 100644 > > > --- a/mm/kasan/report.c > > > +++ b/mm/kasan/report.c > > > @@ -105,9 +105,13 @@ static void end_report(unsigned long *flags) > > > kasan_enable_current(); > > > } > > > > > > -static void print_track(struct kasan_track *track, const char *prefix) > > > +static void print_track(struct kasan_track *track, const char *prefix, > > > + bool is_callrcu) > > > { > > > - pr_err("%s by task %u:\n", prefix, track->pid); > > > + if (is_callrcu) > > > + pr_err("%s:\n", prefix); > > > + else > > > + pr_err("%s by task %u:\n", prefix, track->pid); > > > if (track->stack) { > > > unsigned long *entries; > > > unsigned int nr_entries; > > > @@ -187,11 +191,20 @@ static void describe_object(struct kmem_cache *cache, void *object, > > > if (cache->flags & SLAB_KASAN) { > > > struct kasan_track *free_track; > > > > > > - print_track(&alloc_info->alloc_track, "Allocated"); > > > + print_track(&alloc_info->alloc_track, "Allocated", false); > > > pr_err("\n"); > > > free_track = kasan_get_free_track(cache, object, tag); > > > - print_track(free_track, "Freed"); > > > + print_track(free_track, "Freed", false); > > > pr_err("\n"); > > > + > > > + if (IS_ENABLED(CONFIG_KASAN_GENERIC)) { > > > + free_track = kasan_get_aux_stack(alloc_info, 0); > > > + print_track(free_track, "First call_rcu() call stack", true); > > > + pr_err("\n"); > > > + free_track = kasan_get_aux_stack(alloc_info, 1); > > > + print_track(free_track, "Last call_rcu() call stack", true); > > > + pr_err("\n"); > > > + } > > > } > > > > > > describe_object_addr(cache, object, addr); > > > -- > > I> 2.18.0 > > -- > You received this message because you are subscribed to the Google Groups "kasan-dev" group. > To unsubscribe from this group and stop receiving emails from it, send an email to kasan-dev+unsubscribe@googlegroups.com. > To view this discussion on the web visit https://groups.google.com/d/msgid/kasan-dev/1589250993.19238.22.camel%40mtksdccf07.