Implement serial validation for selection requests

This change tracks, for each wlr_seat_client, the most recent serial
numbers which were sent to the client. When the client makes a
selection request, wlroots now verifies that the serial number
associated with the selection request was actually provided to that
specific client. This ensures that the client that was most
recently interacted with always has priority for its copy selection
requests, and that no other clients can incorrectly use a larger serial
value and "steal" the role of having the copy selection.

Also, the code used to determine when a given selection is superseded
by a newer request uses < instead of <= to allow clients to make
multiple selection requests with the same serial number and have the
last one hold.

To limit memory use, a ring buffer is used to store runs of sequential
serial numbers, and all serial numbers earlier than the start of the
ring buffer are assumed to be valid. Faking very old serials is
unlikely to be disruptive.

Assuming all clients are correctly written, the only additional
constraint which this patch should impose is that serial numbers
are now bound to seats: clients may not receive a serial number
from an input event on one seat and then use that to request
copy-selection on another seat.
This commit is contained in:
Manuel Stoeckl 2019-06-27 15:43:58 -04:00 committed by Drew DeVault
parent fb739b8293
commit edb30a6828
13 changed files with 125 additions and 27 deletions

View file

@ -41,7 +41,7 @@ static void data_device_set_selection(struct wl_client *client,
struct wlr_data_source *wlr_source =
source != NULL ? &source->source : NULL;
wlr_seat_request_set_selection(seat_client->seat, wlr_source, serial);
wlr_seat_request_set_selection(seat_client->seat, seat_client, wlr_source, serial);
}
static void data_device_start_drag(struct wl_client *client,
@ -142,11 +142,18 @@ void seat_client_send_selection(struct wlr_seat_client *seat_client) {
}
void wlr_seat_request_set_selection(struct wlr_seat *seat,
struct wlr_seat_client *client,
struct wlr_data_source *source, uint32_t serial) {
if (client && !wlr_serial_maybe_valid(&client->serials, serial)) {
wlr_log(WLR_DEBUG, "Rejecting set_selection request, "
"serial %"PRIu32" was never given to client", serial);
return;
}
if (seat->selection_source &&
seat->selection_serial - serial < UINT32_MAX / 2) {
wlr_log(WLR_DEBUG, "Rejecting set_selection request, invalid serial "
"(%"PRIu32" <= %"PRIu32")", serial, seat->selection_serial);
serial - seat->selection_serial > UINT32_MAX / 2) {
wlr_log(WLR_DEBUG, "Rejecting set_selection request, serial indicates superseded "
"(%"PRIu32" < %"PRIu32")", serial, seat->selection_serial);
return;
}

View file

@ -370,3 +370,36 @@ bool wlr_seat_validate_grab_serial(struct wlr_seat *seat, uint32_t serial) {
// serial == seat->touch_state.grab_serial;
return true;
}
void wlr_serial_add(struct wlr_serial_ringset *set, uint32_t serial) {
if (set->count == 0 || set->data[set->end].max_incl + 1 != serial) {
set->count++;
if (set->count > WLR_SERIAL_RINGSET_SIZE) {
set->count = WLR_SERIAL_RINGSET_SIZE;
}
set->end = (set->end + 1) % WLR_SERIAL_RINGSET_SIZE;
set->data[set->end].min_incl = serial;
set->data[set->end].max_incl = serial;
} else {
set->data[set->end].max_incl = serial;
}
}
bool wlr_serial_maybe_valid(struct wlr_serial_ringset *set, uint32_t serial) {
for (int i = 0; i < set->count; i++) {
int j = (set->end - i + WLR_SERIAL_RINGSET_SIZE) % WLR_SERIAL_RINGSET_SIZE;
if (set->data[j].max_incl - serial > UINT32_MAX / 2) {
return false;
}
if (serial - set->data[j].min_incl <= UINT32_MAX / 2) {
return true;
}
}
return true;
}
uint32_t wlr_seat_client_next_serial(struct wlr_seat_client *client) {
uint32_t serial = wl_display_next_serial(wl_client_get_display(client->client));
wlr_serial_add(&client->serials, serial);
return serial;
}

View file

@ -72,7 +72,7 @@ void wlr_seat_keyboard_send_key(struct wlr_seat *wlr_seat, uint32_t time,
return;
}
uint32_t serial = wl_display_next_serial(wlr_seat->display);
uint32_t serial = wlr_seat_client_next_serial(client);
struct wl_resource *resource;
wl_resource_for_each(resource, &client->keyboards) {
if (seat_client_from_keyboard_resource(resource) == NULL) {
@ -201,7 +201,7 @@ void wlr_seat_keyboard_send_modifiers(struct wlr_seat *seat,
return;
}
uint32_t serial = wl_display_next_serial(seat->display);
uint32_t serial = wlr_seat_client_next_serial(client);
struct wl_resource *resource;
wl_resource_for_each(resource, &client->keyboards) {
if (seat_client_from_keyboard_resource(resource) == NULL) {
@ -240,7 +240,7 @@ void wlr_seat_keyboard_enter(struct wlr_seat *seat,
// leave the previously entered surface
if (focused_client != NULL && focused_surface != NULL) {
uint32_t serial = wl_display_next_serial(seat->display);
uint32_t serial = wlr_seat_client_next_serial(focused_client);
struct wl_resource *resource;
wl_resource_for_each(resource, &focused_client->keyboards) {
if (seat_client_from_keyboard_resource(resource) == NULL) {
@ -263,7 +263,7 @@ void wlr_seat_keyboard_enter(struct wlr_seat *seat,
}
*p = keycodes[i];
}
uint32_t serial = wl_display_next_serial(seat->display);
uint32_t serial = wlr_seat_client_next_serial(client);
struct wl_resource *resource;
wl_resource_for_each(resource, &client->keyboards) {
if (seat_client_from_keyboard_resource(resource) == NULL) {

View file

@ -149,7 +149,7 @@ void wlr_seat_pointer_enter(struct wlr_seat *wlr_seat,
// leave the previously entered surface
if (focused_client != NULL && focused_surface != NULL) {
uint32_t serial = wl_display_next_serial(wlr_seat->display);
uint32_t serial = wlr_seat_client_next_serial(focused_client);
struct wl_resource *resource;
wl_resource_for_each(resource, &focused_client->pointers) {
if (wlr_seat_client_from_pointer_resource(resource) == NULL) {
@ -163,7 +163,7 @@ void wlr_seat_pointer_enter(struct wlr_seat *wlr_seat,
// enter the current surface
if (client != NULL && surface != NULL) {
uint32_t serial = wl_display_next_serial(wlr_seat->display);
uint32_t serial = wlr_seat_client_next_serial(client);
struct wl_resource *resource;
wl_resource_for_each(resource, &client->pointers) {
if (wlr_seat_client_from_pointer_resource(resource) == NULL) {
@ -242,7 +242,7 @@ uint32_t wlr_seat_pointer_send_button(struct wlr_seat *wlr_seat, uint32_t time,
return 0;
}
uint32_t serial = wl_display_next_serial(wlr_seat->display);
uint32_t serial = wlr_seat_client_next_serial(client);
struct wl_resource *resource;
wl_resource_for_each(resource, &client->pointers) {
if (wlr_seat_client_from_pointer_resource(resource) == NULL) {

View file

@ -278,7 +278,7 @@ uint32_t wlr_seat_touch_send_down(struct wlr_seat *seat,
return 0;
}
uint32_t serial = wl_display_next_serial(seat->display);
uint32_t serial = wlr_seat_client_next_serial(point->client);
struct wl_resource *resource;
wl_resource_for_each(resource, &point->client->touches) {
if (seat_client_from_touch_resource(resource) == NULL) {
@ -299,7 +299,7 @@ void wlr_seat_touch_send_up(struct wlr_seat *seat, uint32_t time, int32_t touch_
return;
}
uint32_t serial = wl_display_next_serial(seat->display);
uint32_t serial = wlr_seat_client_next_serial(point->client);
struct wl_resource *resource;
wl_resource_for_each(resource, &point->client->touches) {
if (seat_client_from_touch_resource(resource) == NULL) {

View file

@ -343,7 +343,7 @@ static void control_handle_set_selection(struct wl_client *client,
}
if (source == NULL) {
wlr_seat_request_set_selection(device->seat, NULL,
wlr_seat_request_set_selection(device->seat, NULL, NULL,
wl_display_next_serial(device->seat->display));
return;
@ -377,7 +377,7 @@ static void control_handle_set_selection(struct wl_client *client,
source->finalized = true;
wlr_seat_request_set_selection(device->seat, wlr_source,
wlr_seat_request_set_selection(device->seat, NULL, wlr_source,
wl_display_next_serial(device->seat->display));
}
@ -396,7 +396,7 @@ static void control_handle_set_primary_selection(struct wl_client *client,
}
if (source == NULL) {
wlr_seat_request_set_primary_selection(device->seat, NULL,
wlr_seat_request_set_primary_selection(device->seat, NULL, NULL,
wl_display_next_serial(device->seat->display));
return;
@ -430,7 +430,7 @@ static void control_handle_set_primary_selection(struct wl_client *client,
source->finalized = true;
wlr_seat_request_set_primary_selection(device->seat, wlr_source,
wlr_seat_request_set_primary_selection(device->seat, NULL, wlr_source,
wl_display_next_serial(device->seat->display));
}

View file

@ -217,7 +217,10 @@ static void device_handle_set_selection(struct wl_client *client,
source = &client_source->source;
}
wlr_seat_request_set_primary_selection(device->seat, source, serial);
struct wlr_seat_client *seat_client =
wlr_seat_client_for_wl_client(device->seat, client);
wlr_seat_request_set_primary_selection(device->seat, seat_client, source, serial);
}
static void device_handle_destroy(struct wl_client *client,

View file

@ -42,11 +42,18 @@ void wlr_primary_selection_source_send(
void wlr_seat_request_set_primary_selection(struct wlr_seat *seat,
struct wlr_seat_client *client,
struct wlr_primary_selection_source *source, uint32_t serial) {
if (seat->primary_selection_source &&
seat->primary_selection_serial - serial < UINT32_MAX / 2) {
if (client && !wlr_serial_maybe_valid(&client->serials, serial)) {
wlr_log(WLR_DEBUG, "Rejecting set_primary_selection request, "
"invalid serial (%"PRIu32" <= %"PRIu32")",
"serial %"PRIu32" was never given to client", serial);
return;
}
if (seat->primary_selection_source &&
serial - seat->primary_selection_serial > UINT32_MAX / 2) {
wlr_log(WLR_DEBUG, "Rejecting set_primary_selection request, "
"serial indicates superseded (%"PRIu32" < %"PRIu32")",
serial, seat->primary_selection_serial);
return;
}

View file

@ -217,7 +217,10 @@ static void device_handle_set_selection(struct wl_client *client,
source = &client_source->source;
}
wlr_seat_request_set_primary_selection(device->seat, source, serial);
struct wlr_seat_client *seat_client =
wlr_seat_client_for_wl_client(device->seat, client);
wlr_seat_request_set_primary_selection(device->seat, seat_client, source, serial);
}
static void device_handle_destroy(struct wl_client *client,