diff options
author | Frederic Weisbecker <fweisbec@gmail.com> | 2015-05-06 12:04:23 -0400 |
---|---|---|
committer | Ingo Molnar <mingo@kernel.org> | 2015-05-07 06:02:50 -0400 |
commit | aed5ed47724f6a7453fa62e3c90f3cee93edbfe3 (patch) | |
tree | 216d3c4a8d7bfa2204052e89790f352ad0dcd1c1 | |
parent | 0e1dc4274828f64fcb56fc7b950acdc5ff7a395f (diff) |
context_tracking: Protect against recursion
Context tracking recursion can happen when an exception triggers
in the middle of a call to a context tracking probe.
This special case can be caused by vmalloc faults. If an access
to a memory area allocated by vmalloc happens in the middle of
context_tracking_enter(), we may run into an endless fault loop
because the exception in turn calls context_tracking_enter()
which faults on the same vmalloc'ed memory, triggering an
exception again, etc...
Some rare crashes have been reported so lets protect against
this with a recursion counter.
Reported-by: Dave Jones <davej@redhat.com>
Signed-off-by: Frederic Weisbecker <fweisbec@gmail.com>
Reviewed-by: Rik van Riel <riel@redhat.com>
Acked-by: Peter Zijlstra (Intel) <peterz@infradead.org>
Cc: Borislav Petkov <bp@alien8.de>
Cc: Chris Metcalf <cmetcalf@ezchip.com>
Cc: H. Peter Anvin <hpa@zytor.com>
Cc: Martin Schwidefsky <schwidefsky@de.ibm.com>
Cc: Mike Galbraith <umgwanakikbuti@gmail.com>
Cc: Oleg Nesterov <oleg@redhat.com>
Cc: Paul E. McKenney <paulmck@linux.vnet.ibm.com>
Cc: Rafael J . Wysocki <rafael.j.wysocki@intel.com>
Cc: Thomas Gleixner <tglx@linutronix.de>
Link: http://lkml.kernel.org/r/1430928266-24888-2-git-send-email-fweisbec@gmail.com
Signed-off-by: Ingo Molnar <mingo@kernel.org>
-rw-r--r-- | include/linux/context_tracking_state.h | 1 | ||||
-rw-r--r-- | kernel/context_tracking.c | 29 |
2 files changed, 30 insertions, 0 deletions
diff --git a/include/linux/context_tracking_state.h b/include/linux/context_tracking_state.h index 6b7b96a32b75..678ecdf90cf6 100644 --- a/include/linux/context_tracking_state.h +++ b/include/linux/context_tracking_state.h | |||
@@ -12,6 +12,7 @@ struct context_tracking { | |||
12 | * may be further optimized using static keys. | 12 | * may be further optimized using static keys. |
13 | */ | 13 | */ |
14 | bool active; | 14 | bool active; |
15 | int recursion; | ||
15 | enum ctx_state { | 16 | enum ctx_state { |
16 | CONTEXT_KERNEL = 0, | 17 | CONTEXT_KERNEL = 0, |
17 | CONTEXT_USER, | 18 | CONTEXT_USER, |
diff --git a/kernel/context_tracking.c b/kernel/context_tracking.c index 72d59a1a6eb6..5b11a10e196a 100644 --- a/kernel/context_tracking.c +++ b/kernel/context_tracking.c | |||
@@ -38,6 +38,25 @@ void context_tracking_cpu_set(int cpu) | |||
38 | } | 38 | } |
39 | } | 39 | } |
40 | 40 | ||
41 | static bool context_tracking_recursion_enter(void) | ||
42 | { | ||
43 | int recursion; | ||
44 | |||
45 | recursion = __this_cpu_inc_return(context_tracking.recursion); | ||
46 | if (recursion == 1) | ||
47 | return true; | ||
48 | |||
49 | WARN_ONCE((recursion < 1), "Invalid context tracking recursion value %d\n", recursion); | ||
50 | __this_cpu_dec(context_tracking.recursion); | ||
51 | |||
52 | return false; | ||
53 | } | ||
54 | |||
55 | static void context_tracking_recursion_exit(void) | ||
56 | { | ||
57 | __this_cpu_dec(context_tracking.recursion); | ||
58 | } | ||
59 | |||
41 | /** | 60 | /** |
42 | * context_tracking_enter - Inform the context tracking that the CPU is going | 61 | * context_tracking_enter - Inform the context tracking that the CPU is going |
43 | * enter user or guest space mode. | 62 | * enter user or guest space mode. |
@@ -75,6 +94,9 @@ void context_tracking_enter(enum ctx_state state) | |||
75 | WARN_ON_ONCE(!current->mm); | 94 | WARN_ON_ONCE(!current->mm); |
76 | 95 | ||
77 | local_irq_save(flags); | 96 | local_irq_save(flags); |
97 | if (!context_tracking_recursion_enter()) | ||
98 | goto out_irq_restore; | ||
99 | |||
78 | if ( __this_cpu_read(context_tracking.state) != state) { | 100 | if ( __this_cpu_read(context_tracking.state) != state) { |
79 | if (__this_cpu_read(context_tracking.active)) { | 101 | if (__this_cpu_read(context_tracking.active)) { |
80 | /* | 102 | /* |
@@ -105,6 +127,8 @@ void context_tracking_enter(enum ctx_state state) | |||
105 | */ | 127 | */ |
106 | __this_cpu_write(context_tracking.state, state); | 128 | __this_cpu_write(context_tracking.state, state); |
107 | } | 129 | } |
130 | context_tracking_recursion_exit(); | ||
131 | out_irq_restore: | ||
108 | local_irq_restore(flags); | 132 | local_irq_restore(flags); |
109 | } | 133 | } |
110 | NOKPROBE_SYMBOL(context_tracking_enter); | 134 | NOKPROBE_SYMBOL(context_tracking_enter); |
@@ -139,6 +163,9 @@ void context_tracking_exit(enum ctx_state state) | |||
139 | return; | 163 | return; |
140 | 164 | ||
141 | local_irq_save(flags); | 165 | local_irq_save(flags); |
166 | if (!context_tracking_recursion_enter()) | ||
167 | goto out_irq_restore; | ||
168 | |||
142 | if (__this_cpu_read(context_tracking.state) == state) { | 169 | if (__this_cpu_read(context_tracking.state) == state) { |
143 | if (__this_cpu_read(context_tracking.active)) { | 170 | if (__this_cpu_read(context_tracking.active)) { |
144 | /* | 171 | /* |
@@ -153,6 +180,8 @@ void context_tracking_exit(enum ctx_state state) | |||
153 | } | 180 | } |
154 | __this_cpu_write(context_tracking.state, CONTEXT_KERNEL); | 181 | __this_cpu_write(context_tracking.state, CONTEXT_KERNEL); |
155 | } | 182 | } |
183 | context_tracking_recursion_exit(); | ||
184 | out_irq_restore: | ||
156 | local_irq_restore(flags); | 185 | local_irq_restore(flags); |
157 | } | 186 | } |
158 | NOKPROBE_SYMBOL(context_tracking_exit); | 187 | NOKPROBE_SYMBOL(context_tracking_exit); |