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>
dev
Derek Foreman 8 years ago
parent f8f7fd69df
commit 76829fc4ea
  1. 3
      libweston/compositor.h
  2. 58
      libweston/input.c

@ -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;

@ -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);

Loading…
Cancel
Save