input: Send unique keymap file descriptors to clients
Client may map any file descriptor opened for writing with PROT_WRITE themselves. On linux, even a read-only file descriptor to an unlinked file can be re-opened with write permission through /proc/self/fd. The only way to prevent this is to create a memfd which is subsequently write-sealed. Unfortunately this prevents clients from mapping with MAP_SHARED, which is already in widespread usage. To isolate and protect the keymap, whilst allowing MAP_SHARED clients to continue to work, use a unique file descriptor for each wl_keyboard resource. Reviewed-by: Daniel Stone <daniels@collabora.com>
This commit is contained in:
@@ -714,9 +714,8 @@ weston_touch_start_drag(struct weston_touch *touch,
|
|||||||
|
|
||||||
struct weston_xkb_info {
|
struct weston_xkb_info {
|
||||||
struct xkb_keymap *keymap;
|
struct xkb_keymap *keymap;
|
||||||
int keymap_fd;
|
|
||||||
size_t keymap_size;
|
size_t keymap_size;
|
||||||
char *keymap_area;
|
char *keymap_string;
|
||||||
int32_t ref_count;
|
int32_t ref_count;
|
||||||
xkb_mod_index_t shift_mod;
|
xkb_mod_index_t shift_mod;
|
||||||
xkb_mod_index_t caps_mod;
|
xkb_mod_index_t caps_mod;
|
||||||
|
|||||||
+26
-32
@@ -2084,11 +2084,31 @@ WL_EXPORT void
|
|||||||
weston_keyboard_send_keymap(struct weston_keyboard *kbd, struct wl_resource *resource)
|
weston_keyboard_send_keymap(struct weston_keyboard *kbd, struct wl_resource *resource)
|
||||||
{
|
{
|
||||||
struct weston_xkb_info *xkb_info = kbd->xkb_info;
|
struct weston_xkb_info *xkb_info = kbd->xkb_info;
|
||||||
|
void *area;
|
||||||
|
int fd;
|
||||||
|
|
||||||
|
fd = os_create_anonymous_file(xkb_info->keymap_size);
|
||||||
|
if (fd < 0) {
|
||||||
|
weston_log("creating a keymap file for %lu bytes failed: %m\n",
|
||||||
|
(unsigned long) xkb_info->keymap_size);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
area = mmap(NULL, xkb_info->keymap_size, PROT_READ | PROT_WRITE,
|
||||||
|
MAP_SHARED, fd, 0);
|
||||||
|
if (area == MAP_FAILED) {
|
||||||
|
weston_log("failed to mmap() %lu bytes\n",
|
||||||
|
(unsigned long) xkb_info->keymap_size);
|
||||||
|
goto err_mmap;
|
||||||
|
}
|
||||||
|
strcpy(area, xkb_info->keymap_string);
|
||||||
|
munmap(area, xkb_info->keymap_size);
|
||||||
wl_keyboard_send_keymap(resource,
|
wl_keyboard_send_keymap(resource,
|
||||||
WL_KEYBOARD_KEYMAP_FORMAT_XKB_V1,
|
WL_KEYBOARD_KEYMAP_FORMAT_XKB_V1,
|
||||||
xkb_info->keymap_fd,
|
fd,
|
||||||
xkb_info->keymap_size);
|
xkb_info->keymap_size);
|
||||||
|
err_mmap:
|
||||||
|
close(fd);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
@@ -3126,10 +3146,8 @@ weston_xkb_info_destroy(struct weston_xkb_info *xkb_info)
|
|||||||
|
|
||||||
xkb_keymap_unref(xkb_info->keymap);
|
xkb_keymap_unref(xkb_info->keymap);
|
||||||
|
|
||||||
if (xkb_info->keymap_area)
|
if (xkb_info->keymap_string)
|
||||||
munmap(xkb_info->keymap_area, xkb_info->keymap_size);
|
free(xkb_info->keymap_string);
|
||||||
if (xkb_info->keymap_fd >= 0)
|
|
||||||
close(xkb_info->keymap_fd);
|
|
||||||
free(xkb_info);
|
free(xkb_info);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -3157,8 +3175,6 @@ weston_xkb_info_create(struct xkb_keymap *keymap)
|
|||||||
xkb_info->keymap = xkb_keymap_ref(keymap);
|
xkb_info->keymap = xkb_keymap_ref(keymap);
|
||||||
xkb_info->ref_count = 1;
|
xkb_info->ref_count = 1;
|
||||||
|
|
||||||
char *keymap_str;
|
|
||||||
|
|
||||||
xkb_info->shift_mod = xkb_keymap_mod_get_index(xkb_info->keymap,
|
xkb_info->shift_mod = xkb_keymap_mod_get_index(xkb_info->keymap,
|
||||||
XKB_MOD_NAME_SHIFT);
|
XKB_MOD_NAME_SHIFT);
|
||||||
xkb_info->caps_mod = xkb_keymap_mod_get_index(xkb_info->keymap,
|
xkb_info->caps_mod = xkb_keymap_mod_get_index(xkb_info->keymap,
|
||||||
@@ -3183,38 +3199,16 @@ weston_xkb_info_create(struct xkb_keymap *keymap)
|
|||||||
xkb_info->scroll_led = xkb_keymap_led_get_index(xkb_info->keymap,
|
xkb_info->scroll_led = xkb_keymap_led_get_index(xkb_info->keymap,
|
||||||
XKB_LED_NAME_SCROLL);
|
XKB_LED_NAME_SCROLL);
|
||||||
|
|
||||||
keymap_str = xkb_keymap_get_as_string(xkb_info->keymap,
|
xkb_info->keymap_string = xkb_keymap_get_as_string(xkb_info->keymap,
|
||||||
XKB_KEYMAP_FORMAT_TEXT_V1);
|
XKB_KEYMAP_FORMAT_TEXT_V1);
|
||||||
if (keymap_str == NULL) {
|
if (xkb_info->keymap_string == NULL) {
|
||||||
weston_log("failed to get string version of keymap\n");
|
weston_log("failed to get string version of keymap\n");
|
||||||
goto err_keymap;
|
goto err_keymap;
|
||||||
}
|
}
|
||||||
xkb_info->keymap_size = strlen(keymap_str) + 1;
|
xkb_info->keymap_size = strlen(xkb_info->keymap_string) + 1;
|
||||||
|
|
||||||
xkb_info->keymap_fd = os_create_anonymous_file(xkb_info->keymap_size);
|
|
||||||
if (xkb_info->keymap_fd < 0) {
|
|
||||||
weston_log("creating a keymap file for %lu bytes failed: %m\n",
|
|
||||||
(unsigned long) xkb_info->keymap_size);
|
|
||||||
goto err_keymap_str;
|
|
||||||
}
|
|
||||||
|
|
||||||
xkb_info->keymap_area = mmap(NULL, xkb_info->keymap_size,
|
|
||||||
PROT_READ | PROT_WRITE,
|
|
||||||
MAP_SHARED, xkb_info->keymap_fd, 0);
|
|
||||||
if (xkb_info->keymap_area == MAP_FAILED) {
|
|
||||||
weston_log("failed to mmap() %lu bytes\n",
|
|
||||||
(unsigned long) xkb_info->keymap_size);
|
|
||||||
goto err_dev_zero;
|
|
||||||
}
|
|
||||||
strcpy(xkb_info->keymap_area, keymap_str);
|
|
||||||
free(keymap_str);
|
|
||||||
|
|
||||||
return xkb_info;
|
return xkb_info;
|
||||||
|
|
||||||
err_dev_zero:
|
|
||||||
close(xkb_info->keymap_fd);
|
|
||||||
err_keymap_str:
|
|
||||||
free(keymap_str);
|
|
||||||
err_keymap:
|
err_keymap:
|
||||||
xkb_keymap_unref(xkb_info->keymap);
|
xkb_keymap_unref(xkb_info->keymap);
|
||||||
free(xkb_info);
|
free(xkb_info);
|
||||||
|
|||||||
Reference in New Issue
Block a user