From: peterz@infradead.org
To: Marco Elver <elver@google.com>
Cc: jgross@suse.com, fenghua.yu@intel.com, yu-cheng.yu@intel.com,
"Luck, Tony" <tony.luck@intel.com>,
Dave Hansen <dave.hansen@linux.intel.com>,
syzkaller-bugs <syzkaller-bugs@googlegroups.com>,
LKML <linux-kernel@vger.kernel.org>,
kasan-dev <kasan-dev@googlegroups.com>,
virtualization@lists.linux-foundation.org,
Ingo Molnar <mingo@redhat.com>, Borislav Petkov <bp@alien8.de>,
"H. Peter Anvin" <hpa@zytor.com>,
Thomas Gleixner <tglx@linutronix.de>,
syzbot <syzbot+8db9e1ecde74e590a657@syzkaller.appspotmail.com>,
the arch/x86 maintainers <x86@kernel.org>
Subject: Re: [PATCH] x86/paravirt: Add missing noinstr to arch_local*() helpers
Date: Thu, 6 Aug 2020 13:32:36 +0200 [thread overview]
Message-ID: <20200806113236.GZ2674@hirez.programming.kicks-ass.net> (raw)
In-Reply-To: <20200806074723.GA2364872@elver.google.com>
On Thu, Aug 06, 2020 at 09:47:23AM +0200, Marco Elver wrote:
> Testing my hypothesis that raw then nested non-raw
> local_irq_save/restore() breaks IRQ state tracking -- see the reproducer
> below. This is at least 1 case I can think of that we're bound to hit.
Aaargh!
> diff --git a/init/main.c b/init/main.c
> index 15bd0efff3df..0873319dcff4 100644
> --- a/init/main.c
> +++ b/init/main.c
> @@ -1041,6 +1041,22 @@ asmlinkage __visible void __init start_kernel(void)
> sfi_init_late();
> kcsan_init();
>
> + /* DEBUG CODE */
> + lockdep_assert_irqs_enabled(); /* Pass. */
> + {
> + unsigned long flags1;
> + raw_local_irq_save(flags1);
This disables IRQs but doesn't trace..
> + {
> + unsigned long flags2;
> + lockdep_assert_irqs_enabled(); /* Pass - expectedly blind. */
Indeed, we didn't trace the above disable, so software state is still
on.
> + local_irq_save(flags2);
So here we save IRQ state, and unconditionally disable IRQs and trace
them disabled.
> + lockdep_assert_irqs_disabled(); /* Pass. */
> + local_irq_restore(flags2);
But here, we restore IRQ state to 'disabled' and explicitly trace it
disabled *again* (which is a bit daft, but whatever).
> + }
> + raw_local_irq_restore(flags1);
This then restores the IRQ state to enable, but no tracing.
> + }
> + lockdep_assert_irqs_enabled(); /* FAIL! */
And we're out of sync... :/
/me goes ponder things...
How's something like this then?
---
include/linux/sched.h | 3 ---
kernel/kcsan/core.c | 62 ++++++++++++++++++++++++++++++++++++---------------
2 files changed, 44 insertions(+), 21 deletions(-)
diff --git a/include/linux/sched.h b/include/linux/sched.h
index 06ec60462af0..2f5aef57e687 100644
--- a/include/linux/sched.h
+++ b/include/linux/sched.h
@@ -1193,9 +1193,6 @@ struct task_struct {
#ifdef CONFIG_KCSAN
struct kcsan_ctx kcsan_ctx;
-#ifdef CONFIG_TRACE_IRQFLAGS
- struct irqtrace_events kcsan_save_irqtrace;
-#endif
#endif
#ifdef CONFIG_FUNCTION_GRAPH_TRACER
diff --git a/kernel/kcsan/core.c b/kernel/kcsan/core.c
index 9147ff6a12e5..9c4436bf0561 100644
--- a/kernel/kcsan/core.c
+++ b/kernel/kcsan/core.c
@@ -291,17 +291,50 @@ static inline unsigned int get_delay(void)
0);
}
-void kcsan_save_irqtrace(struct task_struct *task)
+/*
+ * KCSAN hooks are everywhere, which means they're NMI like for interrupt
+ * tracing. In order to present a 'normal' as possible context to the code
+ * called by KCSAN when reporting errors we need to update the irq-tracing
+ * state.
+ *
+ * Save and restore the IRQ state trace touched by KCSAN, since KCSAN's
+ * runtime is entered for every memory access, and potentially useful
+ * information is lost if dirtied by KCSAN.
+ */
+
+struct kcsan_irq_state {
+ unsigned long flags;
+#ifdef CONFIG_TRACE_IRQFLAGS
+ int hardirqs;
+ struct irqtrace_events irqtrace;
+#endif
+};
+
+void kcsan_save_irqtrace(struct kcsan_irq_state *irq_state)
{
#ifdef CONFIG_TRACE_IRQFLAGS
- task->kcsan_save_irqtrace = task->irqtrace;
+ irq_state->irqtrace = task->irqtrace;
+ irq_state->hardirq = lockdep_hardirqs_enabled();
#endif
+ if (!kcsan_interrupt_watcher) {
+ raw_local_irq_save(irq_state->flags);
+ lockdep_hardirqs_off(CALLER_ADDR0);
+ }
}
-void kcsan_restore_irqtrace(struct task_struct *task)
+void kcsan_restore_irqtrace(struct kcsan_irq_state *irq_state)
{
+ if (!kcsan_interrupt_watcher) {
+#ifdef CONFIG_TRACE_IRQFLAGS
+ if (irq_state->hardirqs) {
+ lockdep_hardirqs_on_prepare(CALLER_ADDR0);
+ lockdep_hardirqs_on(CALLER_ADDR0);
+ }
+#endif
+ raw_local_irq_restore(irq_state->flags);
+ }
#ifdef CONFIG_TRACE_IRQFLAGS
- task->irqtrace = task->kcsan_save_irqtrace;
+ task->irqtrace = irq_state->irqtrace;
#endif
}
@@ -350,11 +383,13 @@ static noinline void kcsan_found_watchpoint(const volatile void *ptr,
flags = user_access_save();
if (consumed) {
- kcsan_save_irqtrace(current);
+ struct kcsan_irq_state irqstate;
+
+ kcsan_save_irqtrace(&irqstate);
kcsan_report(ptr, size, type, KCSAN_VALUE_CHANGE_MAYBE,
KCSAN_REPORT_CONSUMED_WATCHPOINT,
watchpoint - watchpoints);
- kcsan_restore_irqtrace(current);
+ kcsan_restore_irqtrace(&irqstate);
} else {
/*
* The other thread may not print any diagnostics, as it has
@@ -387,7 +422,7 @@ kcsan_setup_watchpoint(const volatile void *ptr, size_t size, int type)
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 = 0;
+ struct kcsan_irq_state irqstate;
/*
* Always reset kcsan_skip counter in slow-path to avoid underflow; see
@@ -412,14 +447,7 @@ kcsan_setup_watchpoint(const volatile void *ptr, size_t size, int type)
goto out;
}
- /*
- * Save and restore the IRQ state trace touched by KCSAN, since KCSAN's
- * runtime is entered for every memory access, and potentially useful
- * information is lost if dirtied by KCSAN.
- */
- kcsan_save_irqtrace(current);
- if (!kcsan_interrupt_watcher)
- local_irq_save(irq_flags);
+ kcsan_save_irqtrace(&irqstate);
watchpoint = insert_watchpoint((unsigned long)ptr, size, is_write);
if (watchpoint == NULL) {
@@ -559,9 +587,7 @@ kcsan_setup_watchpoint(const volatile void *ptr, size_t size, int type)
remove_watchpoint(watchpoint);
kcsan_counter_dec(KCSAN_COUNTER_USED_WATCHPOINTS);
out_unlock:
- if (!kcsan_interrupt_watcher)
- local_irq_restore(irq_flags);
- kcsan_restore_irqtrace(current);
+ kcsan_restore_irqtrace(&irqstate);
out:
user_access_restore(ua_flags);
}
_______________________________________________
Virtualization mailing list
Virtualization@lists.linux-foundation.org
https://lists.linuxfoundation.org/mailman/listinfo/virtualization
WARNING: multiple messages have this Message-ID (diff)
From: peterz@infradead.org
To: Marco Elver <elver@google.com>
Cc: Borislav Petkov <bp@alien8.de>,
Dave Hansen <dave.hansen@linux.intel.com>,
fenghua.yu@intel.com, "H. Peter Anvin" <hpa@zytor.com>,
LKML <linux-kernel@vger.kernel.org>,
Ingo Molnar <mingo@redhat.com>,
syzkaller-bugs <syzkaller-bugs@googlegroups.com>,
Thomas Gleixner <tglx@linutronix.de>,
"Luck, Tony" <tony.luck@intel.com>,
the arch/x86 maintainers <x86@kernel.org>,
yu-cheng.yu@intel.com, jgross@suse.com, sdeep@vmware.com,
virtualization@lists.linux-foundation.org,
kasan-dev <kasan-dev@googlegroups.com>,
syzbot <syzbot+8db9e1ecde74e590a657@syzkaller.appspotmail.com>
Subject: Re: [PATCH] x86/paravirt: Add missing noinstr to arch_local*() helpers
Date: Thu, 6 Aug 2020 13:32:36 +0200 [thread overview]
Message-ID: <20200806113236.GZ2674@hirez.programming.kicks-ass.net> (raw)
In-Reply-To: <20200806074723.GA2364872@elver.google.com>
On Thu, Aug 06, 2020 at 09:47:23AM +0200, Marco Elver wrote:
> Testing my hypothesis that raw then nested non-raw
> local_irq_save/restore() breaks IRQ state tracking -- see the reproducer
> below. This is at least 1 case I can think of that we're bound to hit.
Aaargh!
> diff --git a/init/main.c b/init/main.c
> index 15bd0efff3df..0873319dcff4 100644
> --- a/init/main.c
> +++ b/init/main.c
> @@ -1041,6 +1041,22 @@ asmlinkage __visible void __init start_kernel(void)
> sfi_init_late();
> kcsan_init();
>
> + /* DEBUG CODE */
> + lockdep_assert_irqs_enabled(); /* Pass. */
> + {
> + unsigned long flags1;
> + raw_local_irq_save(flags1);
This disables IRQs but doesn't trace..
> + {
> + unsigned long flags2;
> + lockdep_assert_irqs_enabled(); /* Pass - expectedly blind. */
Indeed, we didn't trace the above disable, so software state is still
on.
> + local_irq_save(flags2);
So here we save IRQ state, and unconditionally disable IRQs and trace
them disabled.
> + lockdep_assert_irqs_disabled(); /* Pass. */
> + local_irq_restore(flags2);
But here, we restore IRQ state to 'disabled' and explicitly trace it
disabled *again* (which is a bit daft, but whatever).
> + }
> + raw_local_irq_restore(flags1);
This then restores the IRQ state to enable, but no tracing.
> + }
> + lockdep_assert_irqs_enabled(); /* FAIL! */
And we're out of sync... :/
/me goes ponder things...
How's something like this then?
---
include/linux/sched.h | 3 ---
kernel/kcsan/core.c | 62 ++++++++++++++++++++++++++++++++++++---------------
2 files changed, 44 insertions(+), 21 deletions(-)
diff --git a/include/linux/sched.h b/include/linux/sched.h
index 06ec60462af0..2f5aef57e687 100644
--- a/include/linux/sched.h
+++ b/include/linux/sched.h
@@ -1193,9 +1193,6 @@ struct task_struct {
#ifdef CONFIG_KCSAN
struct kcsan_ctx kcsan_ctx;
-#ifdef CONFIG_TRACE_IRQFLAGS
- struct irqtrace_events kcsan_save_irqtrace;
-#endif
#endif
#ifdef CONFIG_FUNCTION_GRAPH_TRACER
diff --git a/kernel/kcsan/core.c b/kernel/kcsan/core.c
index 9147ff6a12e5..9c4436bf0561 100644
--- a/kernel/kcsan/core.c
+++ b/kernel/kcsan/core.c
@@ -291,17 +291,50 @@ static inline unsigned int get_delay(void)
0);
}
-void kcsan_save_irqtrace(struct task_struct *task)
+/*
+ * KCSAN hooks are everywhere, which means they're NMI like for interrupt
+ * tracing. In order to present a 'normal' as possible context to the code
+ * called by KCSAN when reporting errors we need to update the irq-tracing
+ * state.
+ *
+ * Save and restore the IRQ state trace touched by KCSAN, since KCSAN's
+ * runtime is entered for every memory access, and potentially useful
+ * information is lost if dirtied by KCSAN.
+ */
+
+struct kcsan_irq_state {
+ unsigned long flags;
+#ifdef CONFIG_TRACE_IRQFLAGS
+ int hardirqs;
+ struct irqtrace_events irqtrace;
+#endif
+};
+
+void kcsan_save_irqtrace(struct kcsan_irq_state *irq_state)
{
#ifdef CONFIG_TRACE_IRQFLAGS
- task->kcsan_save_irqtrace = task->irqtrace;
+ irq_state->irqtrace = task->irqtrace;
+ irq_state->hardirq = lockdep_hardirqs_enabled();
#endif
+ if (!kcsan_interrupt_watcher) {
+ raw_local_irq_save(irq_state->flags);
+ lockdep_hardirqs_off(CALLER_ADDR0);
+ }
}
-void kcsan_restore_irqtrace(struct task_struct *task)
+void kcsan_restore_irqtrace(struct kcsan_irq_state *irq_state)
{
+ if (!kcsan_interrupt_watcher) {
+#ifdef CONFIG_TRACE_IRQFLAGS
+ if (irq_state->hardirqs) {
+ lockdep_hardirqs_on_prepare(CALLER_ADDR0);
+ lockdep_hardirqs_on(CALLER_ADDR0);
+ }
+#endif
+ raw_local_irq_restore(irq_state->flags);
+ }
#ifdef CONFIG_TRACE_IRQFLAGS
- task->irqtrace = task->kcsan_save_irqtrace;
+ task->irqtrace = irq_state->irqtrace;
#endif
}
@@ -350,11 +383,13 @@ static noinline void kcsan_found_watchpoint(const volatile void *ptr,
flags = user_access_save();
if (consumed) {
- kcsan_save_irqtrace(current);
+ struct kcsan_irq_state irqstate;
+
+ kcsan_save_irqtrace(&irqstate);
kcsan_report(ptr, size, type, KCSAN_VALUE_CHANGE_MAYBE,
KCSAN_REPORT_CONSUMED_WATCHPOINT,
watchpoint - watchpoints);
- kcsan_restore_irqtrace(current);
+ kcsan_restore_irqtrace(&irqstate);
} else {
/*
* The other thread may not print any diagnostics, as it has
@@ -387,7 +422,7 @@ kcsan_setup_watchpoint(const volatile void *ptr, size_t size, int type)
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 = 0;
+ struct kcsan_irq_state irqstate;
/*
* Always reset kcsan_skip counter in slow-path to avoid underflow; see
@@ -412,14 +447,7 @@ kcsan_setup_watchpoint(const volatile void *ptr, size_t size, int type)
goto out;
}
- /*
- * Save and restore the IRQ state trace touched by KCSAN, since KCSAN's
- * runtime is entered for every memory access, and potentially useful
- * information is lost if dirtied by KCSAN.
- */
- kcsan_save_irqtrace(current);
- if (!kcsan_interrupt_watcher)
- local_irq_save(irq_flags);
+ kcsan_save_irqtrace(&irqstate);
watchpoint = insert_watchpoint((unsigned long)ptr, size, is_write);
if (watchpoint == NULL) {
@@ -559,9 +587,7 @@ kcsan_setup_watchpoint(const volatile void *ptr, size_t size, int type)
remove_watchpoint(watchpoint);
kcsan_counter_dec(KCSAN_COUNTER_USED_WATCHPOINTS);
out_unlock:
- if (!kcsan_interrupt_watcher)
- local_irq_restore(irq_flags);
- kcsan_restore_irqtrace(current);
+ kcsan_restore_irqtrace(&irqstate);
out:
user_access_restore(ua_flags);
}
next prev parent reply other threads:[~2020-08-06 11:32 UTC|newest]
Thread overview: 55+ messages / expand[flat|nested] mbox.gz Atom feed top
2020-08-05 7:19 upstream test error: WARNING in __local_bh_enable_ip syzbot
2020-08-05 13:26 ` [PATCH] x86/paravirt: Add missing noinstr to arch_local*() helpers Marco Elver
2020-08-05 13:42 ` peterz
2020-08-05 13:42 ` peterz
2020-08-05 13:59 ` Marco Elver
2020-08-05 14:12 ` peterz
2020-08-05 14:12 ` peterz
2020-08-05 14:17 ` Jürgen Groß
2020-08-05 14:17 ` Jürgen Groß
2020-08-05 14:17 ` peterz
2020-08-05 14:17 ` peterz
2020-08-05 14:36 ` Marco Elver
2020-08-05 17:31 ` Marco Elver
2020-08-06 7:47 ` Marco Elver
2020-08-06 11:32 ` peterz [this message]
2020-08-06 11:32 ` peterz
2020-08-06 13:17 ` Marco Elver
2020-08-06 16:06 ` Marco Elver
2020-08-07 9:01 ` Marco Elver
2020-08-07 9:24 ` Jürgen Groß
2020-08-07 9:24 ` Jürgen Groß
2020-08-07 9:50 ` Marco Elver
2020-08-07 10:35 ` Jürgen Groß
2020-08-07 10:35 ` Jürgen Groß
2020-08-07 11:38 ` Marco Elver
2020-08-07 12:04 ` Jürgen Groß
2020-08-07 12:04 ` Jürgen Groß
2020-08-07 12:08 ` Marco Elver
2020-08-07 15:19 ` Marco Elver
2020-08-11 7:00 ` Marco Elver
2020-08-11 7:04 ` Jürgen Groß
2020-08-11 7:04 ` Jürgen Groß
2020-08-11 7:41 ` Peter Zijlstra
2020-08-11 7:41 ` Peter Zijlstra
2020-08-11 7:57 ` Jürgen Groß
2020-08-11 7:57 ` Jürgen Groß
2020-08-11 8:12 ` Peter Zijlstra
2020-08-11 8:12 ` Peter Zijlstra
2020-08-11 8:18 ` Jürgen Groß
2020-08-11 8:18 ` Jürgen Groß
2020-08-11 8:38 ` Jürgen Groß
2020-08-11 8:38 ` Jürgen Groß
2020-08-11 9:20 ` peterz
2020-08-11 9:20 ` peterz
2020-08-11 9:46 ` peterz
2020-08-11 9:46 ` peterz
2020-08-11 20:17 ` peterz
2020-08-11 20:17 ` peterz
2020-08-12 8:06 ` Marco Elver
2020-08-12 8:18 ` peterz
2020-08-12 8:18 ` peterz
2020-08-12 8:57 ` peterz
2020-08-12 8:57 ` peterz
2020-08-06 21:02 ` kernel test robot
2020-08-06 21:02 ` kernel test robot
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=20200806113236.GZ2674@hirez.programming.kicks-ass.net \
--to=peterz@infradead.org \
--cc=bp@alien8.de \
--cc=dave.hansen@linux.intel.com \
--cc=elver@google.com \
--cc=fenghua.yu@intel.com \
--cc=hpa@zytor.com \
--cc=jgross@suse.com \
--cc=kasan-dev@googlegroups.com \
--cc=linux-kernel@vger.kernel.org \
--cc=mingo@redhat.com \
--cc=syzbot+8db9e1ecde74e590a657@syzkaller.appspotmail.com \
--cc=syzkaller-bugs@googlegroups.com \
--cc=tglx@linutronix.de \
--cc=tony.luck@intel.com \
--cc=virtualization@lists.linux-foundation.org \
--cc=x86@kernel.org \
--cc=yu-cheng.yu@intel.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 an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.