mirror of
https://github.com/torvalds/linux.git
synced 2026-09-14 16:10:02 +02:00
- Prevent the use of exited events as group leaders
- Avoid use-after-free of an event's group leader by promoting detached sibling events to standalone entities and correct related accounting and state transitions -----BEGIN PGP SIGNATURE----- iQIzBAABCgAdFiEEzv7L6UO9uDPlPSfHEsHwGGHeVUoFAmqB6xQACgkQEsHwGGHe VUpWFQ/+KyU6R2DC48ifpChzgTFmQ2gmIjy1IIsWSGmdSbyD5CEWar8hHDRq1X9r peuQizTXTQ2Ze75atTMIBExR6eUFYb2sKA1HpobcGnB96cpK8e2vmxhvXoHgB3IZ TYDXQ1RMPnbyTn2rCQwr5cgSq2Qe7w2tMXMQKzfmDxP1r7cE81zJVFAsRdVVZP75 kvlcCL43pIwzxzP7sDb9bU9lTkW2Sw83dLIp3jBd3iiUpIQpwqV+UG3/fkInV58n L56cYFsovLbvWdxq4oj6cFwRBmvyrYKV0zkT+zW0SO2AzlVhfwCD/o74hxLwrN// Gas0d51uQfWt+5M7s6T0KFQYBfClG4uoIi2yh7zXxWEXyhcNuvmAVrY3xiQxReIi m88+ByWHfBc/mYTHKWJAqb8sHhJiktU52T55ktOJaPNGczA5+O/4alnOg3Kxvw7d CXp1raxJLqDxvd7Ubu/LVjWY96ds+fAaC15ydC6Lh08b9LPhA8rRCavf7NeSOzOD E1NA3QLS3TwbrTboaGqLMmzOLPkBBZ+28PUASg1ZpyfGnw91Ggv/gOVNUgr5PvMU D/gzRCwEYfDdhOrIHzGrfJ1fwj4qFsZ6HSge3sHDj8/BCcQYj/zmlTKe0CHKbaiy D7bkLNwdBc0z+eh5T7UfCYodUu0qiiek5Y0G3q8FVad0mFDYtUA= =moR8 -----END PGP SIGNATURE----- Merge tag 'perf_urgent_for_v7.2' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip Pull perf fixes from Borislav Petkov: - Prevent the use of exited events as group leaders - Avoid use-after-free of an event's group leader by promoting detached sibling events to standalone entities and correct related accounting and state transitions * tag 'perf_urgent_for_v7.2' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip: perf/core: Fix group leader use-after-free after sibling detach perf: Reject exited events as group leaders
This commit is contained in:
commit
9da3fc37f5
|
|
@ -2343,6 +2343,34 @@ static inline struct list_head *get_event_list(struct perf_event *event)
|
|||
&event->pmu_ctx->flexible_active;
|
||||
}
|
||||
|
||||
/* @sibling must already be unlinked from its old leader's sibling_list. */
|
||||
static void perf_promote_sibling_to_leader(struct perf_event *sibling,
|
||||
struct perf_event_context *ctx,
|
||||
int group_caps)
|
||||
{
|
||||
/*
|
||||
* Events that have PERF_EV_CAP_SIBLING require being part of
|
||||
* a group and cannot exist on their own, schedule them out
|
||||
* and move them into the ERROR state. Also see
|
||||
* _perf_event_enable(), it will not be able to recover this
|
||||
* ERROR state.
|
||||
*/
|
||||
if (sibling->event_caps & PERF_EV_CAP_SIBLING)
|
||||
__event_disable(sibling, ctx, PERF_EVENT_STATE_ERROR);
|
||||
|
||||
sibling->group_leader = sibling;
|
||||
sibling->group_caps = group_caps;
|
||||
|
||||
if (sibling->attach_state & PERF_ATTACH_CONTEXT) {
|
||||
add_event_to_groups(sibling, ctx);
|
||||
|
||||
if (sibling->state == PERF_EVENT_STATE_ACTIVE)
|
||||
list_add_tail(&sibling->active_list, get_event_list(sibling));
|
||||
}
|
||||
|
||||
perf_event__header_size(sibling);
|
||||
}
|
||||
|
||||
static void perf_group_detach(struct perf_event *event)
|
||||
{
|
||||
struct perf_event *leader = event->group_leader;
|
||||
|
|
@ -2366,8 +2394,9 @@ static void perf_group_detach(struct perf_event *event)
|
|||
*/
|
||||
if (leader != event) {
|
||||
list_del_init(&event->sibling_list);
|
||||
event->group_leader->nr_siblings--;
|
||||
event->group_leader->group_generation++;
|
||||
leader->nr_siblings--;
|
||||
leader->group_generation++;
|
||||
perf_promote_sibling_to_leader(event, ctx, event->event_caps);
|
||||
goto out;
|
||||
}
|
||||
|
||||
|
|
@ -2377,32 +2406,14 @@ static void perf_group_detach(struct perf_event *event)
|
|||
* to whatever list we are on.
|
||||
*/
|
||||
list_for_each_entry_safe(sibling, tmp, &event->sibling_list, sibling_list) {
|
||||
|
||||
/*
|
||||
* Events that have PERF_EV_CAP_SIBLING require being part of
|
||||
* a group and cannot exist on their own, schedule them out
|
||||
* and move them into the ERROR state. Also see
|
||||
* _perf_event_enable(), it will not be able to recover this
|
||||
* ERROR state.
|
||||
*/
|
||||
if (sibling->event_caps & PERF_EV_CAP_SIBLING)
|
||||
__event_disable(sibling, ctx, PERF_EVENT_STATE_ERROR);
|
||||
|
||||
sibling->group_leader = sibling;
|
||||
list_del_init(&sibling->sibling_list);
|
||||
|
||||
/* Inherit group flags from the previous leader */
|
||||
sibling->group_caps = event->group_caps;
|
||||
|
||||
if (sibling->attach_state & PERF_ATTACH_CONTEXT) {
|
||||
add_event_to_groups(sibling, event->ctx);
|
||||
|
||||
if (sibling->state == PERF_EVENT_STATE_ACTIVE)
|
||||
list_add_tail(&sibling->active_list, get_event_list(sibling));
|
||||
}
|
||||
perf_promote_sibling_to_leader(sibling, ctx, event->group_caps);
|
||||
|
||||
WARN_ON_ONCE(sibling->ctx != event->ctx);
|
||||
}
|
||||
event->nr_siblings = 0;
|
||||
|
||||
out:
|
||||
for_each_sibling_event(tmp, leader)
|
||||
|
|
@ -2592,12 +2603,7 @@ __perf_remove_from_context(struct perf_event *event,
|
|||
if (flags & DETACH_DEAD)
|
||||
state = PERF_EVENT_STATE_DEAD;
|
||||
|
||||
event_sched_out(event, ctx);
|
||||
|
||||
if (event->state > PERF_EVENT_STATE_OFF)
|
||||
perf_cgroup_event_disable(event, ctx);
|
||||
|
||||
perf_event_set_state(event, min(event->state, state));
|
||||
__event_disable(event, ctx, state);
|
||||
|
||||
if (flags & DETACH_GROUP)
|
||||
perf_group_detach(event);
|
||||
|
|
@ -2666,8 +2672,9 @@ static void __event_disable(struct perf_event *event,
|
|||
enum perf_event_state state)
|
||||
{
|
||||
event_sched_out(event, ctx);
|
||||
perf_cgroup_event_disable(event, ctx);
|
||||
perf_event_set_state(event, state);
|
||||
if (event->state > PERF_EVENT_STATE_OFF)
|
||||
perf_cgroup_event_disable(event, ctx);
|
||||
perf_event_set_state(event, min(event->state, state));
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
@ -13972,7 +13979,7 @@ SYSCALL_DEFINE5(perf_event_open,
|
|||
goto err_fd;
|
||||
}
|
||||
group_leader = fd_file(group)->private_data;
|
||||
if (group_leader->state <= PERF_EVENT_STATE_REVOKED) {
|
||||
if (group_leader->state <= PERF_EVENT_STATE_EXIT) {
|
||||
err = -ENODEV;
|
||||
goto err_fd;
|
||||
}
|
||||
|
|
@ -14103,6 +14110,12 @@ SYSCALL_DEFINE5(perf_event_open,
|
|||
if (group_leader->ctx != ctx)
|
||||
goto err_locked;
|
||||
|
||||
/* Recheck under ctx::mutex to serialize against remove-on-exec. */
|
||||
if (group_leader->state <= PERF_EVENT_STATE_EXIT) {
|
||||
err = -ENODEV;
|
||||
goto err_locked;
|
||||
}
|
||||
|
||||
/*
|
||||
* Only a group leader can be exclusive or pinned
|
||||
*/
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user