PERF_EVENT VUL

cve-2016-3768

Due to the duplicate constraints, arch/arm/mach-msm/perf_event_msm_krait_l2.c
drivers marks the event as OFF but returns TRUE to perf_event.c which
goes ahead and allocates the hw_event and enables it.

Since event is marked OFF, kernel events core will try to enable this event
again during next perf_event_enable. Which results in same event enabled
on multiple hw_events. But during the perf_release, event struct is freed
and only one hw_event is released. This results in dereferencing the
invalid pointer and hence the crash.

crash and my kernel log

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
[   50.803798] [idhyt] --- call perf_event_open ---
[ 50.806024] [idhyt] --- in perf_event_alloc ---
[ 50.806300] [idhyt] kzalloc event = e9859c00
[ 50.817401] [idhyt] --- in armpmu_add ---
[ 50.817808] [idhyt] event = e9859c00, hwc(hw_perf_event) = e9859cd0, hw_events(pmu_hw_events) = c11a5a68
[ 50.819088] [idhyt] armpmu->get_event_idx = 0
[ 50.819315] [idhyt] c11a5a68->events[0] = e9859c00
[ 50.820089] [idhyt] call perf_event_open return, event_fd = 3, event = e9859c00, hw_perf_event = e9859cd0
[ 50.820505] [idhyt] --- call perf_event_open ---
[ 50.820736] [idhyt] --- in perf_event_alloc ---
[ 50.820966] [idhyt] kzalloc event = e9859800
[ 50.831431] [idhyt] --- in armpmu_add ---
[ 50.831660] [idhyt] event = e9859800, hwc(hw_perf_event) = e98598d0, hw_events(pmu_hw_events) = c11a5a68
[ 50.832072] [idhyt] --- in msm_l2_test_set_ev_constraint ---
[ 50.832478] [idhyt] l2_pmu_constraints.pmu_bitmap = 0x1, bitmap_t = 0x1
[ 50.832710] [idhyt] perf_event = e9859800, set event->state = PERF_EVENT_STATE_OFF, event->attr.constraint_duplicate = 1
[ 50.833125] [idhyt] armpmu->get_event_idx = 1
[ 50.833354] [idhyt] c11a5a68->events[1] = e9859800
[ 50.834114] [idhyt] call perf_event_open return, event_fd = 4, event = e9859800, hw_perf_event = e98598d0

[ 50.834537] [idhyt] ioclt: PERF_EVENT_IOC_ENABLE
[ 50.835412] [idhyt] --- in armpmu_add ---
[ 50.835821] [idhyt] event = e9859800, hwc(hw_perf_event) = e98598d0, hw_events(pmu_hw_events) = c11a5a68
[ 50.836238] [idhyt] --- in msm_l2_test_set_ev_constraint ---
[ 50.836469] [idhyt] l2_pmu_constraints.pmu_bitmap = 0x1, bitmap_t = 0x1
[ 50.836878] [idhyt] perf_event = e9859800, set event->state = PERF_EVENT_STATE_OFF, event->attr.constraint_duplicate = 1
[ 50.837300] [idhyt] armpmu->get_event_idx = 2
[ 50.837530] [idhyt] c11a5a68->events[2] = e9859800
[ 50.837972] [idhyt] --- in perf_release func
[ 50.838202] [idhyt] event = e9859c00, event->state = 1
[ 50.854583] [idhyt] --- in perf_release func
// // release events[1] !!!!!!
[ 50.854763] [idhyt] event = e9859800, event->state = -1

// // open new perf_event !!!!!!
[ 96.572065] [idhyt] --- call perf_event_open ---
[ 96.572112] [idhyt] --- in perf_event_alloc ---
[ 96.572193] [idhyt] kzalloc event = e9859c00
[ 96.572384] [idhyt] --- in perf_init_event ---
[ 96.572673] [idhyt] --- in armpmu_event_init ---
[ 96.573138] [idhyt] --- in armpmu_reserve_hardware ---
[ 96.573578] [idhyt] armpmu->request_pmu_irq
[ 96.573622] [idhyt] --- in krait_l2_handle_irq ---
[ 96.573662] [idhyt] idx = 1
// // dereferencing the invalid pointer and crash!!!!!!
[ 96.573731] [idhyt] event = krait_l2_pmu_hw_events.events[1] = e9859800
[ 96.573771] [idhyt] armpmu_event_update
[ 96.573841] [idhyt] --- in armpmu_event_update ---
[ 96.573881] [idhyt] event = e9859800, event->state = 538976288, hwc(hw_perf_event) = e98598d0, idx = 1
[ 96.573954] Unable to handle kernel paging request at virtual address 202020d8
[ 96.573994] pgd = eb3fc000
[ 96.574063] [202020d8] *pgd=00000000
[ 96.574142] Internal error: Oops: 5 [#1] PREEMPT SMP ARM
[ 96.574184] CPU: 0 Not tainted (3.4.0-geaa8415-dirty #25)
[ 96.574262] PC is at armpmu_event_update+0x64/0x11c
[ 96.574307] LR is at console_unlock+0x2d8/0x330
[ 96.574378] pc : [<c0116bf4>] lr : [<c01ae924>] psr: 20000193
[ 96.574380] sp : ea739b68 ip : ea739a50 fp : ea739b9c
[ 96.574484] r10: e9859928 r9 : e98598d0 r8 : e9859800
[ 96.574524] r7 : 20202020 r6 : 00000001 r5 : 00000000 r4 : 00000000
[ 96.574594] r3 : 20202020 r2 : 9059756f r1 : 60000193 r0 : 0000006d
[ 96.574634] Flags: nzCv IRQs off FIQs on Mode SVC_32 ISA ARM Segment user
[ 96.574705] Control: 10c5787d Table: 337fc06a DAC: 00000015
[ 96.574774]
[ 96.574776] PC: 0xc0116b74:
[ 96.574847] 6b74 ebffff8b eafffff7 00c95cc4 00c95cbc 00c95d04 00c95c98 00c95cd8 e1a0c00d
[ 96.575241] 6b94 e92ddff0 e24cb004 e24dd00c e52de004 e8bd4000 e1a08000 e59f00f0 e1a09001
[ 96.575608] 6bb4 e1a06002 e08f0000 eb28b0fd e59f00e0 e5982030 e1a03009 e58d6000 e1a01008
[ 96.576008] 6bd4 e08f0000 e598702c eb28b0f5 e5983030 e3530000 ba000029 e289a058 e1ba4f9f
[ 96.576376] 6bf4 e59730b8 e1a00006 e12fff33 e3a03000 e1a02000 f57ff05f e1ba0f9f e3a0c000
[ 96.576775] 6c14 e1300004 01310005 01aacf92 e35c0000 1afffff8 f57ff05f e1510005 01500004
[ 96.577145] 6c34 1affffed e1c709d0 e0524004 e0c35005 e288c038 e0000004 e0011005 e1bc4f9f
[ 96.577543] 6c54 e0944000 e0a55001 e1acef94 e33e0000 1afffff9 e289c070 e1bc4f9f e0544000
[ 96.577941]
[ 96.577942] LR: 0xc01ae8a4:
[ 96.578015] e8a4 e2800004 eb00a841 e1a00004 eb26ab59 e595200c e5953004 e1a00004 e1a01006
[ 96.578382] e8c4 e1520003 0a00000d eb26abe5 ebffff17 e3500000 0a00000d e3a03001 e50b3044
[ 96.578784] e8e4 eaffff6e e1a02000 e51b104c e24b0032 ebfffce9 e51b3048 e5932008 eaffffd2
[ 96.579154] e904 eb26abd7 e51b3044 e3530000 1affffee e51b3038 e3530000 0a000000 ebffff2b
[ 96.579553] e924 e24bd028 e89daff0 e7f001f2 e59f0040 e08f0000 e2800004 eb00a81c e24bd028
[ 96.579955] e944 e89daff0 011022d8 00f9d9b8 00f9d948 010030d4 00f9d934 010030bc 00f9d90c
[ 96.580325] e964 00f9d89c fff5179c 00f9d7dc 011020c0 01002f34 01002ea0 e1a0c00d e92ddff0
[ 96.580723] e984 e24cb004 e24dd074 e52de004 e8bd4000 e59f3630 e1a06000 e59f062c e1a05001
[ 96.581121]

cve-2016-0819

attr.disabled = 0;
attr.constraint_duplicate = 1;

fd =syscall(__NR_perf_event_open, &attr, 0, -1, -1, 0);
enable: event->state = PERF_EVENT_STATE_ACTIVE;

ioctl(fd, PERF_EVENT_IOC_DISABLE, 0);
disable: event->state = PERF_EVENT_STATE_OFF

close(fd);

1
2
3
4
5
6
7
8
9
10
11
12
13
static int perf_release(struct inode *inode, struct file *file)
{

struct perf_event *event = file->private_data;
struct task_struct *owner;
/*
* Event can be in state OFF because of a constraint check.
* Change to ACTIVE so that it gets cleaned up correctly.
*/

if ((event->state == PERF_EVENT_STATE_OFF) &&
event->attr.constraint_duplicate)
event->state = PERF_EVENT_STATE_ACTIVE;
...
}

if (event->attr.constraint_duplicate)
event->state = PERF_EVENT_STATE_ACTIVE;

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
static void
event_sched_out(struct perf_event *event,
struct perf_cpu_context *cpuctx,
struct perf_event_context *ctx)

{

u64 tstamp = perf_event_time(event);
u64 delta;
/*
* An event which could not be activated because of
* filter mismatch still needs to have its timings
* maintained, otherwise bogus information is return
* via read() for time_enabled, time_running:
*/

if (event->state == PERF_EVENT_STATE_INACTIVE
&& !event_filter_match(event)) {
delta = tstamp - event->tstamp_stopped;
event->tstamp_running += delta;
event->tstamp_stopped = tstamp;
}
// !! should return, but event->state = PERF_EVENT_STATE_ACTIVE
if (event->state != PERF_EVENT_STATE_ACTIVE)
return;
event->state = PERF_EVENT_STATE_INACTIVE;
if (event->pending_disable) {
event->pending_disable = 0;
event->state = PERF_EVENT_STATE_OFF;
}
event->tstamp_stopped = tstamp;
event->pmu->del(event, 0); // del again, crash !!!
event->oncpu = -1;
...
}

crash backtrace

1
2
3
4
5
6
7
8
9
10
11
12
[11831.615804] [<c0241d00>] (perf_trace_del+0x20/0x4c) from [<c0247f2c>] (event_sched_out+0xb8/0x13c)
[11831.615890] [<c0247f2c>] (event_sched_out+0xb8/0x13c) from [<c024af0c>] (__perf_remove_from_context+0x58/0x94)
[11831.615977] [<c024af0c>] (__perf_remove_from_context+0x58/0x94) from [<c02495fc>] (remote_function+0x5c/0x68)
[11831.616066] [<c02495fc>] (remote_function+0x5c/0x68) from [<c020403c>] (smp_call_function_single+0x1d4/0x228)
[11831.616158] [<c020403c>] (smp_call_function_single+0x1d4/0x228) from [<c0b0e284>] (perf_remove_from_context+0x90/0x170)
[11831.616247] [<c0b0e284>] (perf_remove_from_context+0x90/0x170) from [<c024bdb8>] (perf_event_release_kernel+0x50/0xa0)
[11831.616333] [<c024bdb8>] (perf_event_release_kernel+0x50/0xa0) from [<c024bee8>] (perf_release+0xe0/0x114)
[11831.616424] [<c024bee8>] (perf_release+0xe0/0x114) from [<c0298bfc>] (fput+0xc8/0x244)
[11831.616479] [<c0298bfc>] (fput+0xc8/0x244) from [<c0295ffc>] (filp_close+0x74/0x9c)
[11831.616563] [<c0295ffc>] (filp_close+0x74/0x9c) from [<c02960fc>] (sys_close+0xd8/0x110)
[11831.616651] [<c02960fc>] (sys_close+0xd8/0x110) from [<c0109b00>] (ret_fast_syscall+0x0/0x30)
[11831.616702] Code: e1a03000 e590101c e5900240 e3520000 (e5812000)

cve-2013-2094

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
struct static_key {
atomic_t enabled;
};

static inline void static_key_slow_inc(struct static_key *key)
{

atomic_inc(&key->enabled);
}

static int perf_swevent_init(struct perf_event *event)
{

int event_id = event->attr.config;
...
// if event_id < 0
if (event_id >= PERF_COUNT_SW_MAX)
return -ENOENT;
if (!event->parent) {
int err;
err = swevent_hlist_get(event);
if (err)
return err;
// could inc op array out of bouds
static_key_slow_inc(&perf_swevent_enabled[event_id]);
event->destroy = sw_perf_event_destroy;
}
return 0;
}