Message ID | 432a89fafce11244287c8af757e73a2eb22a5354.1702339432.git.andreyknvl@google.com |
---|---|
State | New |
Headers |
Return-Path: <linux-kernel-owner@vger.kernel.org> Delivered-To: ouuuleilei@gmail.com Received: by 2002:a59:bcd1:0:b0:403:3b70:6f57 with SMTP id r17csp7419086vqy; Mon, 11 Dec 2023 16:14:53 -0800 (PST) X-Google-Smtp-Source: AGHT+IG5ab0T4LfCsGfwIzUcE25ceHIwmL8DIJ3YbQLLHaLAbz9A6LYBOigiO6ncuvRoAbaZkyPN X-Received: by 2002:a17:90a:ec08:b0:286:d210:1498 with SMTP id l8-20020a17090aec0800b00286d2101498mr2384322pjy.12.1702340093564; Mon, 11 Dec 2023 16:14:53 -0800 (PST) ARC-Seal: i=1; a=rsa-sha256; t=1702340093; cv=none; d=google.com; s=arc-20160816; b=duOgsAiGGgqsO8JmXgQHuwj3P8FOfBxrdUjbPnK1fJcMECvk2dccAotJQnpQHyd+g9 0xeByVmIOhNEQGOGKpQVdAC9wzuUGpxud7NWpqRt+8PSwexCap5pzg8DzXWn8PzrblyP RNyKnoDjNphOBkjrCMXjJtd2Qj8rmlH2g6BNg/yJfo9c9HLuPZCt4Ffbt+1YYU4JbYnY Mf2iVbRRMRC+uJpctMWgbhld6FQZrXk6p2EzS9bdMeVopv+Y94DSqPe7kSNUlgDIq0S5 7/BoT5vewGunJQGpxt/k5Ni2BXcyQHmwXroGZFFdl3/z47bW9seDZFMra+UYglg1iZVA 6NAQ== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=list-id:precedence:content-transfer-encoding:mime-version :references:in-reply-to:message-id:date:subject:cc:to:from :dkim-signature; bh=x4Cn8Rdft153TNfaMCqmpUoRbUij/xgpu9Ka1HxHDYI=; fh=xumGgQ3/A3VHYu1DQDaIWA+Xl29EI5lD+bm6U+z1O60=; b=RQ7/++DF74FAKzMMyX6liNro44kAsEPesbUg1/qHFUM+mSVuxIb0VCPC38lwsyCX2U uZzH6K4ONQBzvv+j5s0qqtyF7gFaQRyFG9jGY6vE2PKMJHu+yqt+5L+r7M0WnUHwSEbP 2BJ0tEZ4YK1GxWpZYUfd1P/HE/tWU65DfOU8bK53e6bLwE+ZHvltNBu2QdFMcndv03RL KtaO+OGCLmPvTxuar13Jrt+zybgqgUph+lWC6wPeL02kT2XmwodEvXeDv4T1fyjDy2Sl 0NZR46DhRhG7oWxAeoZumhJScoF3SWFh+COlkxIhLYxFaRU8Y/v5opI9scy//qg+uvl+ iWSg== ARC-Authentication-Results: i=1; mx.google.com; dkim=pass header.i=@linux.dev header.s=key1 header.b="UvkDjF/5"; spf=pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 23.128.96.38 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=pass (p=NONE sp=NONE dis=NONE) header.from=linux.dev Received: from fry.vger.email (fry.vger.email. [23.128.96.38]) by mx.google.com with ESMTPS id h22-20020a17090ac39600b0028688bdbcc4si8284627pjt.161.2023.12.11.16.14.50 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 11 Dec 2023 16:14:53 -0800 (PST) Received-SPF: pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 23.128.96.38 as permitted sender) client-ip=23.128.96.38; Authentication-Results: mx.google.com; dkim=pass header.i=@linux.dev header.s=key1 header.b="UvkDjF/5"; spf=pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 23.128.96.38 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=pass (p=NONE sp=NONE dis=NONE) header.from=linux.dev Received: from out1.vger.email (depot.vger.email [IPv6:2620:137:e000::3:0]) by fry.vger.email (Postfix) with ESMTP id 7CCBC809EE0D; Mon, 11 Dec 2023 16:14:48 -0800 (PST) X-Virus-Status: Clean X-Virus-Scanned: clamav-milter 0.103.11 at fry.vger.email Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1345433AbjLLAOI (ORCPT <rfc822;dexuan.linux@gmail.com> + 99 others); Mon, 11 Dec 2023 19:14:08 -0500 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:48012 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1345415AbjLLAOF (ORCPT <rfc822;linux-kernel@vger.kernel.org>); Mon, 11 Dec 2023 19:14:05 -0500 Received: from out-174.mta1.migadu.com (out-174.mta1.migadu.com [IPv6:2001:41d0:203:375::ae]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 98EFCAD for <linux-kernel@vger.kernel.org>; Mon, 11 Dec 2023 16:14:11 -0800 (PST) X-Report-Abuse: Please report any abuse attempt to abuse@migadu.com and include these headers. DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=linux.dev; s=key1; t=1702340049; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=x4Cn8Rdft153TNfaMCqmpUoRbUij/xgpu9Ka1HxHDYI=; b=UvkDjF/5ctTo0nGnQabktnmBPXGfus3/X3idORYN6B4D44rXq0KJToDsQKy9BeHFX8qw7b qQCY+W0sZZweOoacCTj/1cWItkpd82s9p4KPKbjHROXaD7+UwOj6FNmJAKaJ2G1O+XKX3R d6piyZ++vR5FIz/37yANY5B0lerp6zk= 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>, Vlastimil Babka <vbabka@suse.cz>, kasan-dev@googlegroups.com, Evgenii Stepanov <eugenis@google.com>, Tetsuo Handa <penguin-kernel@i-love.sakura.ne.jp>, linux-mm@kvack.org, linux-kernel@vger.kernel.org, Andrey Konovalov <andreyknvl@google.com>, syzbot+186b55175d8360728234@syzkaller.appspotmail.com Subject: [PATCH mm 2/4] kasan: handle concurrent kasan_record_aux_stack calls Date: Tue, 12 Dec 2023 01:14:01 +0100 Message-Id: <432a89fafce11244287c8af757e73a2eb22a5354.1702339432.git.andreyknvl@google.com> In-Reply-To: <cover.1702339432.git.andreyknvl@google.com> References: <cover.1702339432.git.andreyknvl@google.com> MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Migadu-Flow: FLOW_OUT X-Spam-Status: No, score=-0.9 required=5.0 tests=DKIM_SIGNED,DKIM_VALID, DKIM_VALID_AU,HEADER_FROM_DIFFERENT_DOMAINS,MAILING_LIST_MULTI, SPF_HELO_NONE,SPF_PASS,T_SCC_BODY_TEXT_LINE autolearn=unavailable autolearn_force=no version=3.4.6 X-Spam-Checker-Version: SpamAssassin 3.4.6 (2021-04-09) on fry.vger.email Precedence: bulk List-ID: <linux-kernel.vger.kernel.org> X-Mailing-List: linux-kernel@vger.kernel.org X-Greylist: Sender passed SPF test, not delayed by milter-greylist-4.6.4 (fry.vger.email [0.0.0.0]); Mon, 11 Dec 2023 16:14:48 -0800 (PST) X-getmail-retrieved-from-mailbox: INBOX X-GMAIL-THRID: 1785032965944094102 X-GMAIL-MSGID: 1785032965944094102 |
Series |
lib/stackdepot, kasan: fixes for stack eviction series
|
|
Commit Message
andrey.konovalov@linux.dev
Dec. 12, 2023, 12:14 a.m. UTC
From: Andrey Konovalov <andreyknvl@google.com> kasan_record_aux_stack can be called concurrently on the same object. This might lead to a race condition when rotating the saved aux stack trace handles. Fix by introducing a spinlock to protect the aux stack trace handles in kasan_record_aux_stack. Reported-by: Tetsuo Handa <penguin-kernel@i-love.sakura.ne.jp> Reported-by: syzbot+186b55175d8360728234@syzkaller.appspotmail.com Closes: https://lore.kernel.org/all/000000000000784b1c060b0074a2@google.com/ Signed-off-by: Andrey Konovalov <andreyknvl@google.com> --- This can be squashed into "kasan: use stack_depot_put for Generic mode" or left standalone. --- mm/kasan/generic.c | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-)
Comments
On Tue, 12 Dec 2023 at 01:14, <andrey.konovalov@linux.dev> wrote: > > From: Andrey Konovalov <andreyknvl@google.com> > > kasan_record_aux_stack can be called concurrently on the same object. > This might lead to a race condition when rotating the saved aux stack > trace handles. > > Fix by introducing a spinlock to protect the aux stack trace handles > in kasan_record_aux_stack. > > Reported-by: Tetsuo Handa <penguin-kernel@i-love.sakura.ne.jp> > Reported-by: syzbot+186b55175d8360728234@syzkaller.appspotmail.com > Closes: https://lore.kernel.org/all/000000000000784b1c060b0074a2@google.com/ > Signed-off-by: Andrey Konovalov <andreyknvl@google.com> > > --- > > This can be squashed into "kasan: use stack_depot_put for Generic mode" > or left standalone. > --- > mm/kasan/generic.c | 15 +++++++++++++-- > 1 file changed, 13 insertions(+), 2 deletions(-) > > diff --git a/mm/kasan/generic.c b/mm/kasan/generic.c > index 54e20b2bc3e1..ca5c75a1866c 100644 > --- a/mm/kasan/generic.c > +++ b/mm/kasan/generic.c > @@ -25,6 +25,7 @@ > #include <linux/sched.h> > #include <linux/sched/task_stack.h> > #include <linux/slab.h> > +#include <linux/spinlock.h> > #include <linux/stackdepot.h> > #include <linux/stacktrace.h> > #include <linux/string.h> > @@ -35,6 +36,8 @@ > #include "kasan.h" > #include "../slab.h" > > +DEFINE_SPINLOCK(aux_lock); No, please don't. > /* > * All functions below always inlined so compiler could > * perform better optimizations in each of __asan_loadX/__assn_storeX > @@ -502,6 +505,8 @@ static void __kasan_record_aux_stack(void *addr, depot_flags_t depot_flags) > struct kmem_cache *cache; > struct kasan_alloc_meta *alloc_meta; > void *object; > + depot_stack_handle_t new_handle, old_handle; > + unsigned long flags; > > if (is_kfence_address(addr) || !slab) > return; > @@ -512,9 +517,15 @@ static void __kasan_record_aux_stack(void *addr, depot_flags_t depot_flags) > if (!alloc_meta) > return; > > - stack_depot_put(alloc_meta->aux_stack[1]); > + new_handle = kasan_save_stack(0, depot_flags); > + > + spin_lock_irqsave(&aux_lock, flags); This is a unnecessary global lock. What's the problem here? As far as I can understand a race is possible where we may end up with duplicated or lost stack handles. Since storing this information is best effort anyway, and bugs are rare, a global lock protecting this is overkill. I'd just accept the racyness and use READ_ONCE() / WRITE_ONCE() just to make sure we don't tear any reads/writes and the depot handles are valid. There are other more complex schemes [1], but I think they are overkill as well. [1]: Since a depot stack handle is just an u32, we can have a union { depot_stack_handle_t handles[2]; atomic64_t atomic_handle; } aux_stack; (BUILD_BUG_ON somewhere if sizeof handles and atomic_handle mismatch.) Then in the code here create the same union and load atomic_handle. Swap handle[1] into handle[0] and write the new one in handles[1]. Then do a cmpxchg loop to store the new atomic_handle. > + old_handle = alloc_meta->aux_stack[1]; > alloc_meta->aux_stack[1] = alloc_meta->aux_stack[0]; > - alloc_meta->aux_stack[0] = kasan_save_stack(0, depot_flags); > + alloc_meta->aux_stack[0] = new_handle; > + spin_unlock_irqrestore(&aux_lock, flags); > + > + stack_depot_put(old_handle); > } > > void kasan_record_aux_stack(void *addr) > -- > 2.25.1 >
On Tue, Dec 12, 2023 at 8:29 PM Marco Elver <elver@google.com> wrote: > > > - stack_depot_put(alloc_meta->aux_stack[1]); > > + new_handle = kasan_save_stack(0, depot_flags); > > + > > + spin_lock_irqsave(&aux_lock, flags); > > This is a unnecessary global lock. What's the problem here? As far as > I can understand a race is possible where we may end up with > duplicated or lost stack handles. Yes, this is the problem. And this leads to refcount underflows in the stack depot code, as we fail to keep precise track of the stack traces. > Since storing this information is best effort anyway, and bugs are > rare, a global lock protecting this is overkill. > > I'd just accept the racyness and use READ_ONCE() / WRITE_ONCE() just > to make sure we don't tear any reads/writes and the depot handles are > valid. This will help with the potential tears but will not help with the refcount issues. > There are other more complex schemes [1], but I think they are > overkill as well. > > [1]: Since a depot stack handle is just an u32, we can have a > > union { > depot_stack_handle_t handles[2]; > atomic64_t atomic_handle; > } aux_stack; > (BUILD_BUG_ON somewhere if sizeof handles and atomic_handle mismatch.) > > Then in the code here create the same union and load atomic_handle. > Swap handle[1] into handle[0] and write the new one in handles[1]. > Then do a cmpxchg loop to store the new atomic_handle. This approach should work. If you prefer, I can do this instead of a spinlock. But we do need some kind of atomicity while rotating the aux handles to make sure nothing gets lost. Thanks!
On Wed, 13 Dec 2023 at 15:40, Andrey Konovalov <andreyknvl@gmail.com> wrote: > > On Tue, Dec 12, 2023 at 8:29 PM Marco Elver <elver@google.com> wrote: > > > > > - stack_depot_put(alloc_meta->aux_stack[1]); > > > + new_handle = kasan_save_stack(0, depot_flags); > > > + > > > + spin_lock_irqsave(&aux_lock, flags); > > > > This is a unnecessary global lock. What's the problem here? As far as > > I can understand a race is possible where we may end up with > > duplicated or lost stack handles. > > Yes, this is the problem. And this leads to refcount underflows in the > stack depot code, as we fail to keep precise track of the stack > traces. > > > Since storing this information is best effort anyway, and bugs are > > rare, a global lock protecting this is overkill. > > > > I'd just accept the racyness and use READ_ONCE() / WRITE_ONCE() just > > to make sure we don't tear any reads/writes and the depot handles are > > valid. > > This will help with the potential tears but will not help with the > refcount issues. > > > There are other more complex schemes [1], but I think they are > > overkill as well. > > > > [1]: Since a depot stack handle is just an u32, we can have a > > > > union { > > depot_stack_handle_t handles[2]; > > atomic64_t atomic_handle; > > } aux_stack; > > (BUILD_BUG_ON somewhere if sizeof handles and atomic_handle mismatch.) > > > > Then in the code here create the same union and load atomic_handle. > > Swap handle[1] into handle[0] and write the new one in handles[1]. > > Then do a cmpxchg loop to store the new atomic_handle. > > This approach should work. If you prefer, I can do this instead of a spinlock. > > But we do need some kind of atomicity while rotating the aux handles > to make sure nothing gets lost. Yes, I think that'd be preferable. Although note that not all 32-bit architectures have 64-bit atomics, so that may be an issue. Another alternative is to have a spinlock next to the aux_stack (it needs to be initialized properly). It'll use up a little more space, but that's for KASAN configs only, so I think it's ok. Certainly better than a global lock.
On Wed, Dec 13, 2023 at 5:51 PM Marco Elver <elver@google.com> wrote: > > > > [1]: Since a depot stack handle is just an u32, we can have a > > > > > > union { > > > depot_stack_handle_t handles[2]; > > > atomic64_t atomic_handle; > > > } aux_stack; > > > (BUILD_BUG_ON somewhere if sizeof handles and atomic_handle mismatch.) > > > > > > Then in the code here create the same union and load atomic_handle. > > > Swap handle[1] into handle[0] and write the new one in handles[1]. > > > Then do a cmpxchg loop to store the new atomic_handle. > > > > This approach should work. If you prefer, I can do this instead of a spinlock. > > > > But we do need some kind of atomicity while rotating the aux handles > > to make sure nothing gets lost. > > Yes, I think that'd be preferable. Although note that not all 32-bit > architectures have 64-bit atomics, so that may be an issue. Another > alternative is to have a spinlock next to the aux_stack (it needs to > be initialized properly). It'll use up a little more space, but that's > for KASAN configs only, so I think it's ok. Certainly better than a > global lock. Ah, hm, actually this is what I indented to do with this change. But somehow my brain glitched out and decided to use a global lock :) I'll change this into a local spinlock in v2. Thank you!
diff --git a/mm/kasan/generic.c b/mm/kasan/generic.c index 54e20b2bc3e1..ca5c75a1866c 100644 --- a/mm/kasan/generic.c +++ b/mm/kasan/generic.c @@ -25,6 +25,7 @@ #include <linux/sched.h> #include <linux/sched/task_stack.h> #include <linux/slab.h> +#include <linux/spinlock.h> #include <linux/stackdepot.h> #include <linux/stacktrace.h> #include <linux/string.h> @@ -35,6 +36,8 @@ #include "kasan.h" #include "../slab.h" +DEFINE_SPINLOCK(aux_lock); + /* * All functions below always inlined so compiler could * perform better optimizations in each of __asan_loadX/__assn_storeX @@ -502,6 +505,8 @@ static void __kasan_record_aux_stack(void *addr, depot_flags_t depot_flags) struct kmem_cache *cache; struct kasan_alloc_meta *alloc_meta; void *object; + depot_stack_handle_t new_handle, old_handle; + unsigned long flags; if (is_kfence_address(addr) || !slab) return; @@ -512,9 +517,15 @@ static void __kasan_record_aux_stack(void *addr, depot_flags_t depot_flags) if (!alloc_meta) return; - stack_depot_put(alloc_meta->aux_stack[1]); + new_handle = kasan_save_stack(0, depot_flags); + + spin_lock_irqsave(&aux_lock, flags); + old_handle = alloc_meta->aux_stack[1]; alloc_meta->aux_stack[1] = alloc_meta->aux_stack[0]; - alloc_meta->aux_stack[0] = kasan_save_stack(0, depot_flags); + alloc_meta->aux_stack[0] = new_handle; + spin_unlock_irqrestore(&aux_lock, flags); + + stack_depot_put(old_handle); } void kasan_record_aux_stack(void *addr)