[PATCH xserver v2] xwayland: Fix use after free of cursors

Olivier Fourdan ofourdan at redhat.com
Tue Nov 22 07:09:48 UTC 2016


Sometimes, Xwayland will try to use a cursor that has just been freed,
leading to a crash when trying to access that cursor data either in
miPointerUpdateSprite() or elsewhere.

This issue is very random and hard to reproduce.

Typical backtraces include:

  miPointerUpdateSprite () at mipointer.c
  mieqProcessInputEvents () at mieq.c
  ProcessInputEvents () at xwayland-input.c
  Dispatch () at dispatch.c
  dix_main () at main.c

or

  miPointerUpdateSprite () at mipointer.c
  mieqProcessInputEvents () at mieq.c
  keyboard_handle_modifiers () at xwayland-input.c
  wl_closure_invoke () at src/connection.c
  dispatch_event () at src/wayland-client.c
  dispatch_queue () at src/wayland-client.c
  wl_display_dispatch_queue_pending () at src/wayland-client.c
  wl_display_dispatch_pending () at src/wayland-client.c
  xwl_read_events () at xwayland.c
  WaitForSomething () at WaitFor.c
  Dispatch () at dispatch.c
  dix_main () at main.c

or

  AnimCurTimerNotify () at animcur.c
  DoTimer () at WaitFor.c
  DoTimers () at WaitFor.c
  check_timers () at WaitFor.c
  WaitForSomething () at WaitFor.c
  Dispatch () at dispatch.c
  dix_main () at main.c

CheckMotion() updates the pointer's cursor based on which window
XYToWindow() returns, and Xwayland implements its own xwl_xy_to_window()
to fake a crossing to the root window when the pointer has left the
Wayland surface but is still within the xwindow.

But Xwayland's xwl_xy_to_window() may not return consistent windows as
it updates the last_xwindow if no match is found.

When an xwindow is unrealized, the last_xwindow and focus_window can be
cleared, which will prevent a match in xwl_xy_to_window() whereas
another call to xwl_xy_to_window() will match the wrong window because
of the previous miss:

 * xwl_unrealize_window() clears the seat's focus_window and
   last_xwindow.
 * xwl_xy_to_window() is called, the last_xwindow (unset) does not match
   the window returned by miXYToWindow()
 * xwl_xy_to_window() returns the window from miXYToWindow() and updates
   the last_xwindow.
 * xwl_xy_to_window() is called again, last_xwindow now matches, and the
   root window is returned.

So two consecutive calls to xwl_xy_to_window() may not return the same
xwindow.

To avoid this issue, update the last_xwindow in enter and leave notify
handlers only, and check in xwl_xy_to_window() if the window found by
the regular miXYToWindow() is a child of the last_xwindow, so that
multiple calls to xwl_xy_to_window() return the same window, being
either the one found by miXYToWindow() or the root window.

Bugzilla: https://bugzilla.redhat.com/show_bug.cgi?id=1385258
Signed-off-by: Olivier Fourdan <ofourdan at redhat.com>
---
 v2: Issue was caused by inconsistent returned value from
     xwl_xy_to_window() after a window is unrealized, fix the
     issue to update the last_xwindow on enter/leave notify handlers.
     Fix was succesfully tested in:
     https://bugzilla.redhat.com/show_bug.cgi?id=1387281    
 
 hw/xwayland/xwayland-input.c | 17 +++++++++++++----
 1 file changed, 13 insertions(+), 4 deletions(-)

diff --git a/hw/xwayland/xwayland-input.c b/hw/xwayland/xwayland-input.c
index 0526122..681bc9d 100644
--- a/hw/xwayland/xwayland-input.c
+++ b/hw/xwayland/xwayland-input.c
@@ -312,6 +312,9 @@ pointer_handle_enter(void *data, struct wl_pointer *pointer,
     dx = xwl_seat->focus_window->window->drawable.x;
     dy = xwl_seat->focus_window->window->drawable.y;
 
+    /* We just entered a new xwindow, forget about the old last xwindow */
+    xwl_seat->last_xwindow = NullWindow;
+
     master = GetMaster(dev, POINTER_OR_FLOAT);
     (*pScreen->SetCursorPosition) (dev, pScreen, dx + sx, dy + sy, TRUE);
 
@@ -360,8 +363,14 @@ pointer_handle_leave(void *data, struct wl_pointer *pointer,
 
     xwl_seat->xwl_screen->serial = serial;
 
-    xwl_seat->focus_window = NULL;
-    CheckMotion(NULL, GetMaster(dev, POINTER_OR_FLOAT));
+    /* The pointer has left a known xwindow, save it for a possible match
+     * in sprite_check_lost_focus()
+     */
+    if (xwl_seat->focus_window) {
+        xwl_seat->last_xwindow = xwl_seat->focus_window->window;
+        xwl_seat->focus_window = NULL;
+        CheckMotion(NULL, GetMaster(dev, POINTER_OR_FLOAT));
+    }
 }
 
 static void
@@ -1256,10 +1265,10 @@ sprite_check_lost_focus(SpritePtr sprite, WindowPtr window)
      */
     if (master->lastSlave == xwl_seat->pointer &&
         xwl_seat->focus_window == NULL &&
-        xwl_seat->last_xwindow == window)
+        xwl_seat->last_xwindow != NullWindow &&
+        IsParent (xwl_seat->last_xwindow, window))
         return TRUE;
 
-    xwl_seat->last_xwindow = window;
     return FALSE;
 }
 
-- 
2.9.3



More information about the xorg-devel mailing list