From: Pavel Machek <pavel@ucw.cz>
To: Mark Rutland <mark.rutland@arm.com>
Cc: Kees Cook <keescook@chromium.org>,
Peter Zijlstra <peterz@infradead.org>,
Arnaldo Carvalho de Melo <acme@redhat.com>,
kernel list <linux-kernel@vger.kernel.org>,
Ingo Molnar <mingo@redhat.com>,
Alexander Shishkin <alexander.shishkin@linux.intel.com>,
"kernel-hardening@lists.openwall.com"
<kernel-hardening@lists.openwall.com>
Subject: Re: [kernel-hardening] rowhammer protection [was Re: Getting interrupt every million cache misses]
Date: Fri, 28 Oct 2016 13:21:36 +0200 [thread overview]
Message-ID: <20161028112136.GA5635@amd> (raw)
In-Reply-To: <20161028095141.GA5806@leverpostej>
[-- Attachment #1: Type: text/plain, Size: 8025 bytes --]
Hi!
> I missed the original, so I've lost some context.
You can read it on lkml, but I guess you did not lose anything
important.
> Has this been tested on a system vulnerable to rowhammer, and if so, was
> it reliable in mitigating the issue?
>
> Which particular attack codebase was it tested against?
I have rowhammer-test here,
commit 9824453fff76e0a3f5d1ac8200bc6c447c4fff57
Author: Mark Seaborn <mseaborn@chromium.org>
. I do not have vulnerable machine near me, so no "real" tests, but
I'm pretty sure it will make the error no longer reproducible with the
newer version. [Help welcome ;-)]
> > +struct perf_event_attr rh_attr = {
> > + .type = PERF_TYPE_HARDWARE,
> > + .config = PERF_COUNT_HW_CACHE_MISSES,
> > + .size = sizeof(struct perf_event_attr),
> > + .pinned = 1,
> > + /* FIXME: it is 1000000 per cpu. */
> > + .sample_period = 500000,
> > +};
>
> I'm not sure that this is general enough to live in core code, because:
Well, I'd like to postpone debate 'where does it live' to the later
stage. The problem is not arch-specific, the solution is not too
arch-specific either. I believe we can use Kconfig to hide it from
users where it does not apply. Anyway, lets decide if it works and
where, first.
> * the precise semantics of performance counter events varies drastically
> across implementations. PERF_COUNT_HW_CACHE_MISSES, might only map to
> one particular level of cache, and/or may not be implemented on all
> cores.
If it maps to one particular cache level, we are fine (or maybe will
trigger protection too often). If some cores are not counted, that's
bad.
> * On some implementations, it may be that the counters are not
> interchangeable, and for those this would take away
> PERF_COUNT_HW_CACHE_MISSES from existing users.
Yup. Note that with this kind of protection, one missing performance
counter is likely to be small problem.
> > + *ts = now;
> > +
> > + /* FIXME msec per usec, reverse logic? */
> > + if (delta < 64 * NSEC_PER_MSEC)
> > + mdelay(56);
> > +}
>
> If I round-robin my attack across CPUs, how much does this help?
See below for new explanation. With 2 CPUs, we are fine. On monster
big-little 8-core machines, we'd probably trigger protection too
often.
Pavel
diff --git a/arch/x86/Kconfig b/arch/x86/Kconfig
index e24e981..c6ffcaf 100644
--- a/arch/x86/Kconfig
+++ b/arch/x86/Kconfig
@@ -315,6 +315,7 @@ config PGTABLE_LEVELS
source "init/Kconfig"
source "kernel/Kconfig.freezer"
+source "kernel/events/Kconfig"
menu "Processor type and features"
diff --git a/kernel/events/Kconfig b/kernel/events/Kconfig
new file mode 100644
index 0000000..7359427
--- /dev/null
+++ b/kernel/events/Kconfig
@@ -0,0 +1,9 @@
+config NOHAMMER
+ tristate "Rowhammer protection"
+ help
+ Enable rowhammer attack prevention. Will degrade system
+ performance under attack so much that attack should not
+ be feasible.
+
+ To compile this driver as a module, choose M here: the
+ module will be called nohammer.
diff --git a/kernel/events/Makefile b/kernel/events/Makefile
index 2925188..03a2785 100644
--- a/kernel/events/Makefile
+++ b/kernel/events/Makefile
@@ -4,6 +4,8 @@ endif
obj-y := core.o ring_buffer.o callchain.o
+obj-$(CONFIG_NOHAMMER) += nohammer.o
+
obj-$(CONFIG_HAVE_HW_BREAKPOINT) += hw_breakpoint.o
obj-$(CONFIG_UPROBES) += uprobes.o
diff --git a/kernel/events/nohammer.c b/kernel/events/nohammer.c
new file mode 100644
index 0000000..d96bacd
--- /dev/null
+++ b/kernel/events/nohammer.c
@@ -0,0 +1,140 @@
+/*
+ * Attempt to prevent rowhammer attack.
+ *
+ * On many new DRAM chips, repeated read access to nearby cells can cause
+ * victim cell to flip bits. Unfortunately, that can be used to gain root
+ * on affected machine, or to execute native code from javascript, escaping
+ * the sandbox.
+ *
+ * Fortunately, a lot of memory accesses is needed between DRAM refresh
+ * cycles. This is rather unusual workload, and we can detect it, and
+ * prevent the DRAM accesses, before bit flips happen.
+ *
+ * Thanks to Peter Zijlstra <peterz@infradead.org>.
+ * Thanks to presentation at blackhat.
+ */
+
+#include <linux/perf_event.h>
+#include <linux/module.h>
+#include <linux/delay.h>
+
+static struct perf_event_attr rh_attr = {
+ .type = PERF_TYPE_HARDWARE,
+ .config = PERF_COUNT_HW_CACHE_MISSES,
+ .size = sizeof(struct perf_event_attr),
+ .pinned = 1,
+ .sample_period = 10000,
+};
+
+/*
+ * How often is the DRAM refreshed. Setting it too high is safe.
+ */
+static int dram_refresh_msec = 64;
+
+static DEFINE_PER_CPU(struct perf_event *, rh_event);
+static DEFINE_PER_CPU(u64, rh_timestamp);
+
+static void rh_overflow(struct perf_event *event, struct perf_sample_data *data, struct pt_regs *regs)
+{
+ u64 *ts = this_cpu_ptr(&rh_timestamp); /* this is NMI context */
+ u64 now = ktime_get_mono_fast_ns();
+ s64 delta = now - *ts;
+
+ *ts = now;
+
+ if (delta < dram_refresh_msec * NSEC_PER_MSEC)
+ mdelay(dram_refresh_msec);
+}
+
+static __init int rh_module_init(void)
+{
+ int cpu;
+
+/*
+ * DRAM refresh is every 64 msec. That is not enough to prevent rowhammer.
+ * Some vendors doubled the refresh rate to 32 msec, that helps a lot, but
+ * does not close the attack completely. 8 msec refresh would probably do
+ * that on almost all chips.
+ *
+ * Thinkpad X60 can produce cca 12,200,000 cache misses a second, that's
+ * 780,800 cache misses per 64 msec window.
+ *
+ * X60 is from generation that is not yet vulnerable from rowhammer, and
+ * is pretty slow machine. That means that this limit is probably very
+ * safe on newer machines.
+ */
+ int cache_misses_per_second = 12200000;
+
+/*
+ * Maximum permitted utilization of DRAM. Setting this to f will mean that
+ * when more than 1/f of maximum cache-miss performance is used, delay will
+ * be inserted, and will have similar effect on rowhammer as refreshing memory
+ * f times more often.
+ *
+ * Setting this to 8 should prevent the rowhammer attack.
+ */
+ int dram_max_utilization_factor = 8;
+
+ /*
+ * Hardware should be able to do approximately this many
+ * misses per refresh
+ */
+ int cache_miss_per_refresh = (cache_misses_per_second * dram_refresh_msec)/1000;
+
+ /*
+ * So we do not want more than this many accesses to DRAM per
+ * refresh.
+ */
+ int cache_miss_limit = cache_miss_per_refresh / dram_max_utilization_factor;
+
+/*
+ * DRAM is shared between CPUs, but these performance counters are per-CPU.
+ */
+ int max_attacking_cpus = 2;
+
+ /*
+ * We ignore counter overflows "too far away", but some of the
+ * events might have actually occurent recently. Thus additional
+ * factor of 2
+ */
+
+ rh_attr.sample_period = cache_miss_limit / (2*max_attacking_cpus);
+
+ printk("Rowhammer protection limit is set to %d cache misses per %d msec\n",
+ (int) rh_attr.sample_period, dram_refresh_msec);
+
+ /* XXX borken vs hotplug */
+
+ for_each_online_cpu(cpu) {
+ struct perf_event *event;
+
+ event = perf_event_create_kernel_counter(&rh_attr, cpu, NULL, rh_overflow, NULL);
+ per_cpu(rh_event, cpu) = event;
+ if (!event) {
+ pr_err("Not enough resources to initialize nohammer on cpu %d\n", cpu);
+ continue;
+ }
+ pr_info("Nohammer initialized on cpu %d\n", cpu);
+ }
+ return 0;
+}
+
+static __exit void rh_module_exit(void)
+{
+ int cpu;
+
+ for_each_online_cpu(cpu) {
+ struct perf_event *event = per_cpu(rh_event, cpu);
+
+ if (event)
+ perf_event_release_kernel(event);
+ }
+ return;
+}
+
+module_init(rh_module_init);
+module_exit(rh_module_exit);
+
+MODULE_DESCRIPTION("Rowhammer protection");
+//MODULE_LICENSE("GPL v2+");
+MODULE_LICENSE("GPL");
--
(english) http://www.livejournal.com/~pavelmachek
(cesky, pictures) http://atrey.karlin.mff.cuni.cz/~pavel/picture/horses/blog.html
[-- Attachment #2: Digital signature --]
[-- Type: application/pgp-signature, Size: 181 bytes --]
next prev parent reply other threads:[~2016-10-28 11:21 UTC|newest]
Thread overview: 56+ messages / expand[flat|nested] mbox.gz Atom feed top
2016-10-26 20:54 Getting interrupt every million cache misses Pavel Machek
2016-10-27 8:28 ` Peter Zijlstra
2016-10-27 8:46 ` Pavel Machek
2016-10-27 9:15 ` Peter Zijlstra
2016-10-27 9:11 ` Pavel Machek
2016-10-27 9:33 ` Peter Zijlstra
2016-10-27 20:40 ` [kernel-hardening] " Kees Cook
2016-10-27 20:40 ` Kees Cook
2016-10-27 21:27 ` [kernel-hardening] rowhammer protection [was Re: Getting interrupt every million cache misses] Pavel Machek
2016-10-27 21:27 ` Pavel Machek
2016-10-28 7:07 ` [kernel-hardening] " Ingo Molnar
2016-10-28 7:07 ` Ingo Molnar
2016-10-28 8:50 ` [kernel-hardening] " Pavel Machek
2016-10-28 8:50 ` Pavel Machek
2016-10-28 8:59 ` [kernel-hardening] " Ingo Molnar
2016-10-28 8:59 ` Ingo Molnar
2016-10-28 11:55 ` [kernel-hardening] " Pavel Machek
2016-10-28 11:55 ` Pavel Machek
2016-10-28 9:04 ` [kernel-hardening] " Peter Zijlstra
2016-10-28 9:04 ` Peter Zijlstra
2016-10-28 9:27 ` [kernel-hardening] " Vegard Nossum
2016-10-28 9:27 ` Vegard Nossum
2016-10-28 9:35 ` [kernel-hardening] " Ingo Molnar
2016-10-28 9:35 ` Ingo Molnar
2016-10-28 9:47 ` [kernel-hardening] " Vegard Nossum
2016-10-28 9:47 ` Vegard Nossum
2016-10-28 9:53 ` [kernel-hardening] " Mark Rutland
2016-10-28 11:27 ` Pavel Machek
2016-10-28 11:27 ` Pavel Machek
2016-10-28 9:51 ` [kernel-hardening] " Mark Rutland
2016-10-28 11:21 ` Pavel Machek [this message]
2016-10-28 14:05 ` Mark Rutland
2016-10-28 14:18 ` Peter Zijlstra
2016-10-28 18:30 ` Pavel Machek
2016-10-28 18:48 ` Peter Zijlstra
2016-11-02 18:13 ` Pavel Machek
2016-10-28 17:27 ` Pavel Machek
2016-10-29 13:06 ` Daniel Gruss
2016-10-29 19:42 ` Pavel Machek
2016-10-29 20:05 ` Daniel Gruss
2016-10-29 20:14 ` Daniel Gruss
2016-10-29 21:05 ` Pavel Machek
2016-10-29 21:07 ` Daniel Gruss
2016-10-29 21:45 ` Pavel Machek
2016-10-29 21:49 ` Daniel Gruss
2016-10-29 22:01 ` Pavel Machek
2016-10-29 22:02 ` Daniel Gruss
2016-10-31 8:27 ` Pavel Machek
2016-10-31 14:47 ` Mark Rutland
2016-10-31 21:13 ` Pavel Machek
2016-10-31 22:09 ` Mark Rutland
2016-11-01 6:33 ` Ingo Molnar
2016-11-01 7:20 ` Daniel Micay
2016-11-01 7:53 ` Daniel Gruss
2016-11-01 8:10 ` Pavel Machek
2016-11-01 8:13 ` Daniel Gruss
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=20161028112136.GA5635@amd \
--to=pavel@ucw.cz \
--cc=acme@redhat.com \
--cc=alexander.shishkin@linux.intel.com \
--cc=keescook@chromium.org \
--cc=kernel-hardening@lists.openwall.com \
--cc=linux-kernel@vger.kernel.org \
--cc=mark.rutland@arm.com \
--cc=mingo@redhat.com \
--cc=peterz@infradead.org \
/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.