Received: by 2002:a25:1506:0:0:0:0:0 with SMTP id 6csp4020248ybv; Mon, 10 Feb 2020 10:44:29 -0800 (PST) X-Google-Smtp-Source: APXvYqxBqQsqD+2JNTLrCd0HI9ZRX8MxlD5YL7+AtFXentgw1V5qB3A/Rs69RfegXW1tTcappY0c X-Received: by 2002:aca:8d5:: with SMTP id 204mr255552oii.141.1581360268896; Mon, 10 Feb 2020 10:44:28 -0800 (PST) ARC-Seal: i=1; a=rsa-sha256; t=1581360268; cv=none; d=google.com; s=arc-20160816; b=KdcICSgFy6LtBq0K9QsyVucqSZrouRnJoyQ5steKd8YFBnbW01u3/fVgd/vOx2hHR4 Il5qTyoJeSjMmjcPuZihysHBmO6BxAnwWbo1wIEV1b78lccEk/JDtaJkhmH+UgO8Gjpo j2Eks/gSlrtpbHQDe+kNX5xIpqPY3Q5urD6cbKfwAcNTRZsNwSu6gyDcCW8GodMTGmHy OPAOIofuFodsAZ7xFTPTUqoC3DPT3VlahOOi2bDEakaJRrA2QGfxkIDnT07AkkDRtbqs x+6X82ftkePhr0XxsM76XRoZvWpNo89JAL+8nEepBJa8Ajy00XcocMahEPrFiMONIuin zxWg== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=list-id:precedence:sender:cc:to:from:subject:references :mime-version:message-id:in-reply-to:date:dkim-signature; bh=/uNmwRP5i1M3BzrZ/s9fqUFQKdmd/YrPMERWgsitzGo=; b=hp1L7AHNbUw/rD8CmCFnIrvjW3NZ/rhJURN0qOmZdjMDPC3mg390ThK3uq/LPr4OSf k2WYe5+F5X/UX8i5xP68zlD6jPU3n8I4TULJvBsEGPpruavLOXY7aJ8hIDv3FNHdnzC2 8Q+dqaZuSUMG5d9PjunsWDcWGHj88dTJysaMrsa7EMCN2TXPYTHSd/0FtKLKS/3bdQCH AcuFkG+AHoODxpKb7l5qU1P3SArIjeE3pcaESPiMpEDx4Ae0V5xSvsoclFKlnulwgNh8 WOik6WJY7NvoAeEToBE1ylhmKJFAyb7FDdnPRZibK4qjA3Irz3k1CCO7zkjTiitNK1Ap vh4w== ARC-Authentication-Results: i=1; mx.google.com; dkim=pass header.i=@google.com header.s=20161025 header.b=JQW96SML; spf=pass (google.com: best guess record for domain of linux-kernel-owner@vger.kernel.org designates 209.132.180.67 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. [209.132.180.67]) by mx.google.com with ESMTP id x16si583985otp.184.2020.02.10.10.44.17; Mon, 10 Feb 2020 10:44:28 -0800 (PST) Received-SPF: pass (google.com: best guess record for domain of linux-kernel-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) client-ip=209.132.180.67; Authentication-Results: mx.google.com; dkim=pass header.i=@google.com header.s=20161025 header.b=JQW96SML; spf=pass (google.com: best guess record for domain of linux-kernel-owner@vger.kernel.org designates 209.132.180.67 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 S1727756AbgBJSnm (ORCPT + 99 others); Mon, 10 Feb 2020 13:43:42 -0500 Received: from mail-wr1-f73.google.com ([209.85.221.73]:56836 "EHLO mail-wr1-f73.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1727363AbgBJSnm (ORCPT ); Mon, 10 Feb 2020 13:43:42 -0500 Received: by mail-wr1-f73.google.com with SMTP id t3so5426711wrm.23 for ; Mon, 10 Feb 2020 10:43:39 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20161025; h=date:in-reply-to:message-id:mime-version:references:subject:from:to :cc; bh=/uNmwRP5i1M3BzrZ/s9fqUFQKdmd/YrPMERWgsitzGo=; b=JQW96SMLaQLCFnJL61NbjvnDLQ1aqfj6DmfiNBH7pvd4munCHagJs755zsjfgIWYcY QiZdnogebkUiswK8uPclWvzbQPV2kQEuAJjA50PuzlaqakOSusfGlwWtLDxFkaLeHZBi OR3GB+nZtLx9obG2xWnuHtMB+TWmmM2sHE/ffzUrac5Vk7QwzDL+cGOq7Mi2CNvzuM9/ 4ULU3Ra5chhP2vdb3totJySR/mgC/qHAwj4IhFb0fGQOxm6Ohk9OpT/WhqD4wT4seMVP T55ewwG2Qhe0Vyyrog43mQDtkVYOEgRemOPCr9xlwYhpS8Ob7rcaCqowh7ldMyVp0W3c YybA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:date:in-reply-to:message-id:mime-version :references:subject:from:to:cc; bh=/uNmwRP5i1M3BzrZ/s9fqUFQKdmd/YrPMERWgsitzGo=; b=VieHUVD6Pttvs1oV5TGKDeg5W9X/HWhLRgkhVHF+fLnWijQrYh3Vj/5i+57nya9q2j AI7ZdKZuca+6uQgfrUjIVplbfzfO0CrTRfgQwc7yQ8gwSBvKn0+stkw7HnMOpmTRmLi1 5iMjebhKqTb8OiMUa7ntxU8r5Xt72ITIL467WPpdNBPA0ouw68USfuWpp32K5T0K//Fo BxLEwrsj+LeNK/byPo2IK75faU9H7Nt6yueXQmvr4NYFqXojSj3sY23X9eKWPrjszS0K 8uJ6vrOq3BHVEMKt/8ihbEGHJc+/+St4vQRFJP3rdRc6qxHUfOKq1VGOP2bbXbUfPZM4 OuBw== X-Gm-Message-State: APjAAAVfUZE4qjTBspjn5/yECEq7Y/ydfcoMl/HTu7wTyyWpWzqwppwR fC/t21K4FLlJOS2Z3oJMod8D8sgAuA== X-Received: by 2002:a5d:4e0a:: with SMTP id p10mr3516566wrt.229.1581360218892; Mon, 10 Feb 2020 10:43:38 -0800 (PST) Date: Mon, 10 Feb 2020 19:43:16 +0100 In-Reply-To: <20200210184317.233039-1-elver@google.com> Message-Id: <20200210184317.233039-4-elver@google.com> Mime-Version: 1.0 References: <20200210184317.233039-1-elver@google.com> X-Mailer: git-send-email 2.25.0.341.g760bfbb309-goog Subject: [PATCH 4/5] kcsan: Add kcsan_set_access_mask() support From: Marco Elver To: elver@google.com Cc: paulmck@kernel.org, andreyknvl@google.com, glider@google.com, dvyukov@google.com, kasan-dev@googlegroups.com, linux-kernel@vger.kernel.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 When setting up an access mask with kcsan_set_access_mask(), KCSAN will only report races if concurrent changes to bits set in access_mask are observed. Conveying access_mask via a separate call avoids introducing overhead in the common-case fast-path. Signed-off-by: Marco Elver --- include/linux/kcsan-checks.h | 11 +++++++++ include/linux/kcsan.h | 5 +++++ init/init_task.c | 1 + kernel/kcsan/core.c | 43 ++++++++++++++++++++++++++++++++---- kernel/kcsan/kcsan.h | 5 +++++ kernel/kcsan/report.c | 13 ++++++++++- 6 files changed, 73 insertions(+), 5 deletions(-) diff --git a/include/linux/kcsan-checks.h b/include/linux/kcsan-checks.h index 8675411c8dbcd..4ef5233ff3f04 100644 --- a/include/linux/kcsan-checks.h +++ b/include/linux/kcsan-checks.h @@ -68,6 +68,16 @@ void kcsan_flat_atomic_end(void); */ void kcsan_atomic_next(int n); +/** + * kcsan_set_access_mask - set access mask + * + * Set the access mask for all accesses for the current context if non-zero. + * Only value changes to bits set in the mask will be reported. + * + * @mask bitmask + */ +void kcsan_set_access_mask(unsigned long mask); + #else /* CONFIG_KCSAN */ static inline void __kcsan_check_access(const volatile void *ptr, size_t size, @@ -78,6 +88,7 @@ static inline void kcsan_nestable_atomic_end(void) { } static inline void kcsan_flat_atomic_begin(void) { } static inline void kcsan_flat_atomic_end(void) { } static inline void kcsan_atomic_next(int n) { } +static inline void kcsan_set_access_mask(unsigned long mask) { } #endif /* CONFIG_KCSAN */ diff --git a/include/linux/kcsan.h b/include/linux/kcsan.h index 7a614ca558f65..3b84606e1e675 100644 --- a/include/linux/kcsan.h +++ b/include/linux/kcsan.h @@ -35,6 +35,11 @@ struct kcsan_ctx { */ int atomic_nest_count; bool in_flat_atomic; + + /* + * Access mask for all accesses if non-zero. + */ + unsigned long access_mask; }; /** diff --git a/init/init_task.c b/init/init_task.c index 2b4fe98b0f095..096191d177d5c 100644 --- a/init/init_task.c +++ b/init/init_task.c @@ -167,6 +167,7 @@ struct task_struct init_task .atomic_next = 0, .atomic_nest_count = 0, .in_flat_atomic = false, + .access_mask = 0, }, #endif #ifdef CONFIG_TRACE_IRQFLAGS diff --git a/kernel/kcsan/core.c b/kernel/kcsan/core.c index 3f89801161d33..589b1e7f0f253 100644 --- a/kernel/kcsan/core.c +++ b/kernel/kcsan/core.c @@ -39,6 +39,7 @@ static DEFINE_PER_CPU(struct kcsan_ctx, kcsan_cpu_ctx) = { .atomic_next = 0, .atomic_nest_count = 0, .in_flat_atomic = false, + .access_mask = 0, }; /* @@ -298,6 +299,15 @@ static noinline void kcsan_found_watchpoint(const volatile void *ptr, if (!kcsan_is_enabled()) return; + + /* + * The access_mask check relies on value-change comparison. To avoid + * reporting a race where e.g. the writer set up the watchpoint, but the + * reader has access_mask!=0, we have to ignore the found watchpoint. + */ + if (get_ctx()->access_mask != 0) + return; + /* * Consume the watchpoint as soon as possible, to minimize the chances * of !consumed. Consuming the watchpoint must always be guarded by @@ -341,6 +351,7 @@ kcsan_setup_watchpoint(const volatile void *ptr, size_t size, int type) u32 _4; u64 _8; } expect_value; + unsigned long access_mask; enum kcsan_value_change value_change = KCSAN_VALUE_CHANGE_MAYBE; unsigned long ua_flags = user_access_save(); unsigned long irq_flags; @@ -435,18 +446,27 @@ kcsan_setup_watchpoint(const volatile void *ptr, size_t size, int type) * Re-read value, and check if it is as expected; if not, we infer a * racy access. */ + access_mask = get_ctx()->access_mask; switch (size) { case 1: expect_value._1 ^= READ_ONCE(*(const u8 *)ptr); + if (access_mask) + expect_value._1 &= (u8)access_mask; break; case 2: expect_value._2 ^= READ_ONCE(*(const u16 *)ptr); + if (access_mask) + expect_value._2 &= (u16)access_mask; break; case 4: expect_value._4 ^= READ_ONCE(*(const u32 *)ptr); + if (access_mask) + expect_value._4 &= (u32)access_mask; break; case 8: expect_value._8 ^= READ_ONCE(*(const u64 *)ptr); + if (access_mask) + expect_value._8 &= (u64)access_mask; break; default: break; /* ignore; we do not diff the values */ @@ -460,11 +480,20 @@ kcsan_setup_watchpoint(const volatile void *ptr, size_t size, int type) if (!remove_watchpoint(watchpoint)) { /* * Depending on the access type, map a value_change of MAYBE to - * TRUE (require reporting). + * TRUE (always report) or FALSE (never report). */ - if (value_change == KCSAN_VALUE_CHANGE_MAYBE && (size > 8 || is_assert)) { - /* Always assume a value-change. */ - value_change = KCSAN_VALUE_CHANGE_TRUE; + if (value_change == KCSAN_VALUE_CHANGE_MAYBE) { + if (access_mask != 0) { + /* + * For access with access_mask, we require a + * value-change, as it is likely that races on + * ~access_mask bits are expected. + */ + value_change = KCSAN_VALUE_CHANGE_FALSE; + } else if (size > 8 || is_assert) { + /* Always assume a value-change. */ + value_change = KCSAN_VALUE_CHANGE_TRUE; + } } /* @@ -622,6 +651,12 @@ void kcsan_atomic_next(int n) } EXPORT_SYMBOL(kcsan_atomic_next); +void kcsan_set_access_mask(unsigned long mask) +{ + get_ctx()->access_mask = mask; +} +EXPORT_SYMBOL(kcsan_set_access_mask); + void __kcsan_check_access(const volatile void *ptr, size_t size, int type) { check_access(ptr, size, type); diff --git a/kernel/kcsan/kcsan.h b/kernel/kcsan/kcsan.h index 83a79b08b550e..892de5120c1b6 100644 --- a/kernel/kcsan/kcsan.h +++ b/kernel/kcsan/kcsan.h @@ -98,6 +98,11 @@ enum kcsan_value_change { */ KCSAN_VALUE_CHANGE_MAYBE, + /* + * Did not observe a value-change, and it is invalid to report the race. + */ + KCSAN_VALUE_CHANGE_FALSE, + /* * The value was observed to change, and the race should be reported. */ diff --git a/kernel/kcsan/report.c b/kernel/kcsan/report.c index 57805035868bc..70ccff816db81 100644 --- a/kernel/kcsan/report.c +++ b/kernel/kcsan/report.c @@ -132,6 +132,9 @@ static bool rate_limit_report(unsigned long frame1, unsigned long frame2) static bool skip_report(enum kcsan_value_change value_change, unsigned long top_frame) { + /* Should never get here if value_change==FALSE. */ + WARN_ON_ONCE(value_change == KCSAN_VALUE_CHANGE_FALSE); + /* * The first call to skip_report always has value_change==TRUE, since we * cannot know the value written of an instrumented access. For the 2nd @@ -475,7 +478,15 @@ void kcsan_report(const volatile void *ptr, size_t size, int access_type, kcsan_disable_current(); if (prepare_report(&flags, ptr, size, access_type, cpu_id, type)) { - if (print_report(ptr, size, access_type, value_change, cpu_id, type) && panic_on_warn) + /* + * Never report if value_change is FALSE, only if we it is + * either TRUE or MAYBE. In case of MAYBE, further filtering may + * be done once we know the full stack trace in print_report(). + */ + bool reported = value_change != KCSAN_VALUE_CHANGE_FALSE && + print_report(ptr, size, access_type, value_change, cpu_id, type); + + if (reported && panic_on_warn) panic("panic_on_warn set ...\n"); release_report(&flags, type); -- 2.25.0.341.g760bfbb309-goog