From: Christopher James Halse Rogers <christopher.halse.rog...@canonical.com>

The SwapBuffers request requires that we trigger the swap at some point
in the future.  Sane drivers implement this by passing this request to
something that will trigger a callback with the buffer pointers
at the appropriate time.

The client can cause those buffers to be freed in X before the trigger
occurs, most easily by quitting.  This leads to a server crash in
the driver when trying to do the swap.

See http://bugs.freedesktop.org/show_bug.cgi?id=29065 for Radeon and
https://bugs.freedesktop.org/show_bug.cgi?id=28080 for Intel.

Signed-off-by: Christopher James Halse Rogers 
<christopher.halse.rog...@canonical.com>
Signed-off-by: Pauli Nieminen <ext-pauli.niemi...@nokia.com>
---
 hw/xfree86/dri2/dri2.c |   68 ++++++++++++++++++++++++++++++++++++------------
 hw/xfree86/dri2/dri2.h |    1 +
 2 files changed, 52 insertions(+), 17 deletions(-)

diff --git a/hw/xfree86/dri2/dri2.c b/hw/xfree86/dri2/dri2.c
index c9fd74f..6a3f0cb 100644
--- a/hw/xfree86/dri2/dri2.c
+++ b/hw/xfree86/dri2/dri2.c
@@ -109,8 +109,10 @@ typedef struct _DRI2Screen {
 } DRI2ScreenRec;
 
 typedef struct _DRI2SwapCompleteDataRec {
-    ClientPtr client;
-    void *data;
+    DRI2BufferPtr      src;
+    DRI2BufferPtr      dest;
+    ClientPtr          client;
+    void *             data;
 } DRI2SwapCompleteDataRec, *DRI2SwapCompleteDataPtr;
 
 static DRI2ScreenPtr
@@ -119,6 +121,23 @@ DRI2GetScreen(ScreenPtr pScreen)
     return dixLookupPrivate(&pScreen->devPrivates, dri2ScreenPrivateKey);
 }
 
+static void
+buffer_ref(DRI2BufferPtr buffer)
+{
+    buffer->refcnt++;
+}
+
+static void
+buffer_unref(DRI2DrawablePtr pPriv, DRI2BufferPtr buffer)
+{
+    DRI2ScreenPtr ds = pPriv->dri2_screen;
+
+    buffer->refcnt--;
+    if (buffer->refcnt == 0) {
+       (*ds->DestroyBuffer)(pPriv, buffer);
+    }
+}
+
 DRI2DrawablePtr
 DRI2GetDrawable(DrawablePtr pDraw)
 {
@@ -337,7 +356,6 @@ DRI2DestroyDrawable(ClientPtr client, DRI2DrawablePtr 
pPriv, XID id)
 static int DRI2DrawableGone(pointer p, XID id)
 {
     DRI2DrawablePtr pPriv = p;
-    DRI2ScreenPtr   ds = pPriv->dri2_screen;
     DRI2DrawableRefPtr ref, next;
     DrawablePtr pDraw = pPriv->drawable;
     int i;
@@ -383,7 +401,7 @@ static int DRI2DrawableGone(pointer p, XID id)
 
     if (pPriv->buffers != NULL) {
        for (i = 0; i < pPriv->bufferCount; i++)
-           (*ds->DestroyBuffer)(pPriv, pPriv->buffers[i]);
+           buffer_unref(pPriv, pPriv->buffers[i]);
 
        free(pPriv->buffers);
     }
@@ -424,6 +442,7 @@ allocate_or_reuse_buffer(DrawablePtr pDraw, DRI2ScreenPtr 
ds,
        || !dimensions_match
        || (pPriv->buffers[old_buf]->format != format)) {
        *buffer = (*ds->CreateBuffer)(pDraw, attachment, format);
+       buffer_ref(*buffer);
        pPriv->serialNumber = DRI2DrawableSerial(pDraw);
        return TRUE;
 
@@ -438,14 +457,12 @@ static void
 update_dri2_drawable_buffers(DRI2DrawablePtr pPriv, DrawablePtr pDraw,
                             DRI2BufferPtr *buffers, int *out_count, int 
*width, int *height)
 {
-    DRI2ScreenPtr   ds = DRI2GetScreen(pDraw->pScreen);
     int i;
 
     if (pPriv->buffers != NULL) {
        for (i = 0; i < pPriv->bufferCount; i++) {
-           if (pPriv->buffers[i] != NULL) {
-               (*ds->DestroyBuffer)(pPriv, pPriv->buffers[i]);
-           }
+           if (pPriv->buffers[i] != NULL)
+               buffer_unref(pPriv, pPriv->buffers[i]);
        }
 
        free(pPriv->buffers);
@@ -581,7 +598,7 @@ err_out:
 
     for (i = 0; i < count; i++) {
            if (buffers[i] != NULL)
-                   (*ds->DestroyBuffer)(pPriv, buffers[i]);
+                   buffer_unref(pPriv, buffers[i]);
     }
 
     free(buffers);
@@ -793,6 +810,14 @@ DRI2WakeClient(ClientPtr client, DRI2DrawablePtr pPriv, 
int frame,
     }
 }
 
+static void
+free_swap_complete_data (DRI2DrawablePtr pPriv, DRI2SwapCompleteDataPtr 
pSwapData)
+{
+    buffer_unref(pPriv, pSwapData->src);
+    buffer_unref(pPriv, pSwapData->dest);
+    free(pSwapData);
+}
+
 void
 DRI2SwapComplete2(DRI2DrawablePtr pPriv, int frame,
                   unsigned int tv_sec, unsigned int tv_usec, int type,
@@ -807,11 +832,6 @@ DRI2SwapComplete2(DRI2DrawablePtr pPriv, int frame,
     pPriv->swap_count++;
     pPriv->refcnt--;
 
-    if (pPriv->refcnt <= 0) {
-       DRI2DrawableGone(pPriv, 0);
-       return;
-    }
-
     if (pDraw) {
        BoxRec          box;
        RegionRec       region;
@@ -835,7 +855,10 @@ DRI2SwapComplete2(DRI2DrawablePtr pPriv, int frame,
 
     DRI2WakeClient(client, pPriv, frame, tv_sec, tv_usec);
 
-    free(pSwapData);
+    free_swap_complete_data(pPriv, pSwapData);
+
+    if (pPriv->refcnt <= 0)
+       DRI2DrawableGone(pPriv, 0);
 }
 
 Bool
@@ -890,6 +913,17 @@ DRI2SwapBuffers(ClientPtr client, DRI2DrawablePtr pPriv, 
CARD64 target_msc,
     if (pSwapData == NULL)
        return BadAlloc;
 
+    /*
+     * Take a reference to the buffers we're exchanging.
+     * This ensures that these buffers will remain valid until the swap
+     * is completed.
+     *
+     * DRI2SwapComplete is required to unref these buffers.
+     */
+    buffer_ref(pSrcBuffer);
+    buffer_ref(pDestBuffer);
+    pSwapData->src = pSrcBuffer;
+    pSwapData->dest = pDestBuffer;
     pSwapData->client = client;
     pSwapData->data = data;
 
@@ -949,11 +983,11 @@ DRI2SwapBuffers(ClientPtr client, DRI2DrawablePtr pPriv, 
CARD64 target_msc,
     pPriv->swapsPending++;
     pPriv->refcnt++;
     ret = (*ds->ScheduleSwap)(client, pDraw, pDestBuffer, pSrcBuffer,
-                             swap_target, divisor, remainder, func, data);
+                             swap_target, divisor, remainder, func, pSwapData);
     if (!ret) {
        pPriv->swapsPending--; /* didn't schedule */
        pPriv->refcnt--;
-       free(pSwapData);
+       free_swap_complete_data(pPriv, pSwapData);
         xf86DrvMsg(pScreen->myNum, X_ERROR,
                   "[DRI2] %s: driver failed to schedule swap\n", __func__);
        return BadDrawable;
diff --git a/hw/xfree86/dri2/dri2.h b/hw/xfree86/dri2/dri2.h
index 88171be..7b49e7f 100644
--- a/hw/xfree86/dri2/dri2.h
+++ b/hw/xfree86/dri2/dri2.h
@@ -44,6 +44,7 @@ typedef struct {
     unsigned int flags;
     unsigned int format;
     void *driverPrivate;
+    unsigned int refcnt;
 } DRI2BufferRec, *DRI2BufferPtr;
 
 struct _DRI2Drawable;
-- 
1.7.0.4

_______________________________________________
xorg-devel@lists.x.org: X.Org development
Archives: http://lists.x.org/archives/xorg-devel
Info: http://lists.x.org/mailman/listinfo/xorg-devel

Reply via email to