diff options
author | Will Deacon <will.deacon@arm.com> | 2013-09-10 05:58:12 -0400 |
---|---|---|
committer | Matt Turner <mattst88@gmail.com> | 2013-11-16 19:37:10 -0500 |
commit | 6e22f8f2e8d81dcab4c40bc229d53388fda63dbc (patch) | |
tree | a06cbec1bfd358372db2647fafe847204d51ebe7 | |
parent | 4914d7b458e35a7db2f9c7dc6eb014620254bbbf (diff) |
alpha: perf: fix out-of-bounds array access triggered from raw event
Vince's perf fuzzer uncovered the following issue on Alpha:
Unable to handle kernel paging request at virtual address fffffbfe4e46a0e8
CPU 0 perf_fuzzer(1278): Oops 0
pc = [<fffffc000031fbc0>] ra = [<fffffc000031ff54>] ps = 0007 Not tainted
pc is at alpha_perf_event_set_period+0x60/0xf0
ra is at alpha_pmu_enable+0x1a4/0x1c0
v0 = 0000000000000000 t0 = 00000000000fffff t1 = fffffc007b3f5800
t2 = fffffbff275faa94 t3 = ffffffffc9b9bd89 t4 = fffffbfe4e46a098
t5 = 0000000000000020 t6 = fffffbfe4e46a0b8 t7 = fffffc007f4c8000
s0 = 0000000000000000 s1 = fffffc0001b0c018 s2 = fffffc0001b0c020
s3 = fffffc007b3f5800 s4 = 0000000000000001 s5 = ffffffffc9b9bd85
s6 = 0000000000000001
a0 = 0000000000000006 a1 = fffffc007b3f5908 a2 = fffffbfe4e46a098
a3 = 00000005000108c0 a4 = 0000000000000000 a5 = 0000000000000000
t8 = 0000000000000001 t9 = 0000000000000001 t10= 0000000027829f6f
t11= 0000000000000020 pv = fffffc000031fb60 at = fffffc0000950900
gp = fffffc0000940900 sp = fffffc007f4cbca8
Disabling lock debugging due to kernel taint
Trace:
[<fffffc000031ff54>] alpha_pmu_enable+0x1a4/0x1c0
[<fffffc000039f4e8>] perf_pmu_enable+0x48/0x60
[<fffffc00003a0d6c>] __perf_install_in_context+0x15c/0x230
[<fffffc000039d1f0>] remote_function+0x80/0xa0
[<fffffc00003a0c10>] __perf_install_in_context+0x0/0x230
[<fffffc000037b7e4>] smp_call_function_single+0x1b4/0x1d0
[<fffffc000039bb70>] task_function_call+0x60/0x80
[<fffffc00003a0c10>] __perf_install_in_context+0x0/0x230
[<fffffc000039bb44>] task_function_call+0x34/0x80
[<fffffc000039d3fc>] perf_install_in_context+0x9c/0x150
[<fffffc00003a0c10>] __perf_install_in_context+0x0/0x230
[<fffffc00003a5100>] SYSC_perf_event_open+0x360/0xac0
[<fffffc00003110c4>] entSys+0xa4/0xc0
This is due to the raw event encoding being used as an index directly
into the ev67_mapping array, rather than being validated against the
ev67_pmc_event_type enumeration instead. Unlike other architectures,
which allow raw events to propagate into the hardware counters with
little interference, the limited number of events on Alpha and the
strict event <-> counter relationships mean that raw events actually
correspond to the Linux-specific Alpha events, rather than anything
defined by the architecture.
This patch adds a new callback to alpha_pmu_t for validating the raw
event encoding with the Linux event types for the PMU, preventing the
out-of-bounds array access.
Cc: Peter Zijlstra <a.p.zijlstra@chello.nl>
Acked-by: Michael Cree <mcree@orcon.net.nz>
Acked-by: Matt Turner <mattst88@gmail.com>
Signed-off-by: Will Deacon <will.deacon@arm.com>
-rw-r--r-- | arch/alpha/kernel/perf_event.c | 15 |
1 files changed, 13 insertions, 2 deletions
diff --git a/arch/alpha/kernel/perf_event.c b/arch/alpha/kernel/perf_event.c index d821b17047e0..c52e7f0ee5f6 100644 --- a/arch/alpha/kernel/perf_event.c +++ b/arch/alpha/kernel/perf_event.c | |||
@@ -83,6 +83,8 @@ struct alpha_pmu_t { | |||
83 | long pmc_left[3]; | 83 | long pmc_left[3]; |
84 | /* Subroutine for allocation of PMCs. Enforces constraints. */ | 84 | /* Subroutine for allocation of PMCs. Enforces constraints. */ |
85 | int (*check_constraints)(struct perf_event **, unsigned long *, int); | 85 | int (*check_constraints)(struct perf_event **, unsigned long *, int); |
86 | /* Subroutine for checking validity of a raw event for this PMU. */ | ||
87 | int (*raw_event_valid)(u64 config); | ||
86 | }; | 88 | }; |
87 | 89 | ||
88 | /* | 90 | /* |
@@ -203,6 +205,12 @@ success: | |||
203 | } | 205 | } |
204 | 206 | ||
205 | 207 | ||
208 | static int ev67_raw_event_valid(u64 config) | ||
209 | { | ||
210 | return config >= EV67_CYCLES && config < EV67_LAST_ET; | ||
211 | }; | ||
212 | |||
213 | |||
206 | static const struct alpha_pmu_t ev67_pmu = { | 214 | static const struct alpha_pmu_t ev67_pmu = { |
207 | .event_map = ev67_perfmon_event_map, | 215 | .event_map = ev67_perfmon_event_map, |
208 | .max_events = ARRAY_SIZE(ev67_perfmon_event_map), | 216 | .max_events = ARRAY_SIZE(ev67_perfmon_event_map), |
@@ -211,7 +219,8 @@ static const struct alpha_pmu_t ev67_pmu = { | |||
211 | .pmc_count_mask = {EV67_PCTR_0_COUNT_MASK, EV67_PCTR_1_COUNT_MASK, 0}, | 219 | .pmc_count_mask = {EV67_PCTR_0_COUNT_MASK, EV67_PCTR_1_COUNT_MASK, 0}, |
212 | .pmc_max_period = {(1UL<<20) - 1, (1UL<<20) - 1, 0}, | 220 | .pmc_max_period = {(1UL<<20) - 1, (1UL<<20) - 1, 0}, |
213 | .pmc_left = {16, 4, 0}, | 221 | .pmc_left = {16, 4, 0}, |
214 | .check_constraints = ev67_check_constraints | 222 | .check_constraints = ev67_check_constraints, |
223 | .raw_event_valid = ev67_raw_event_valid, | ||
215 | }; | 224 | }; |
216 | 225 | ||
217 | 226 | ||
@@ -609,7 +618,9 @@ static int __hw_perf_event_init(struct perf_event *event) | |||
609 | } else if (attr->type == PERF_TYPE_HW_CACHE) { | 618 | } else if (attr->type == PERF_TYPE_HW_CACHE) { |
610 | return -EOPNOTSUPP; | 619 | return -EOPNOTSUPP; |
611 | } else if (attr->type == PERF_TYPE_RAW) { | 620 | } else if (attr->type == PERF_TYPE_RAW) { |
612 | ev = attr->config & 0xff; | 621 | if (!alpha_pmu->raw_event_valid(attr->config)) |
622 | return -EINVAL; | ||
623 | ev = attr->config; | ||
613 | } else { | 624 | } else { |
614 | return -EOPNOTSUPP; | 625 | return -EOPNOTSUPP; |
615 | } | 626 | } |