st/mesa: fix context use-after-free problem in st_renderbuffer_delete()
[profile/ivi/mesa.git] / src / mesa / state_tracker / st_cb_fbo.c
index 88c6fa2..755697c 100644 (file)
@@ -113,7 +113,7 @@ st_renderbuffer_alloc_storage(struct gl_context * ctx,
    struct pipe_context *pipe = st->pipe;
    struct pipe_screen *screen = st->pipe->screen;
    struct st_renderbuffer *strb = st_renderbuffer(rb);
-   enum pipe_format format;
+   enum pipe_format format = PIPE_FORMAT_NONE;
    struct pipe_surface surf_tmpl;
    struct pipe_resource templ;
 
@@ -133,8 +133,36 @@ st_renderbuffer_alloc_storage(struct gl_context * ctx,
    pipe_surface_reference( &strb->surface, NULL );
    pipe_resource_reference( &strb->texture, NULL );
 
-   format = st_choose_renderbuffer_format(screen, internalFormat,
-                                          rb->NumSamples);
+   /* Handle multisample renderbuffers first.
+    *
+    * From ARB_framebuffer_object:
+    *   If <samples> is zero, then RENDERBUFFER_SAMPLES is set to zero.
+    *   Otherwise <samples> represents a request for a desired minimum
+    *   number of samples. Since different implementations may support
+    *   different sample counts for multisampled rendering, the actual
+    *   number of samples allocated for the renderbuffer image is
+    *   implementation dependent.  However, the resulting value for
+    *   RENDERBUFFER_SAMPLES is guaranteed to be greater than or equal
+    *   to <samples> and no more than the next larger sample count supported
+    *   by the implementation.
+    *
+    * So let's find the supported number of samples closest to NumSamples.
+    * (NumSamples == 1) is treated the same as (NumSamples == 0).
+    */
+   if (rb->NumSamples > 1) {
+      unsigned i;
+
+      for (i = rb->NumSamples; i <= ctx->Const.MaxSamples; i++) {
+         format = st_choose_renderbuffer_format(screen, internalFormat, i);
+
+         if (format != PIPE_FORMAT_NONE) {
+            rb->NumSamples = i;
+            break;
+         }
+      }
+   } else {
+      format = st_choose_renderbuffer_format(screen, internalFormat, 0);
+   }
 
    /* Not setting gl_renderbuffer::Format here will cause
     * FRAMEBUFFER_UNSUPPORTED and ValidateFramebuffer will not be called.
@@ -197,14 +225,16 @@ st_renderbuffer_alloc_storage(struct gl_context * ctx,
  * gl_renderbuffer::Delete()
  */
 static void
-st_renderbuffer_delete(struct gl_renderbuffer *rb)
+st_renderbuffer_delete(struct gl_context *ctx, struct gl_renderbuffer *rb)
 {
    struct st_renderbuffer *strb = st_renderbuffer(rb);
-   ASSERT(strb);
-   pipe_surface_reference(&strb->surface, NULL);
+   struct st_context *st = st_context(ctx);
+   struct pipe_context *pipe = st->pipe;
+
+   pipe_surface_release(pipe, &strb->surface);
    pipe_resource_reference(&strb->texture, NULL);
    free(strb->data);
-   free(strb);
+   _mesa_delete_renderbuffer(ctx, rb);
 }
 
 
@@ -263,13 +293,21 @@ st_new_renderbuffer_fb(enum pipe_format format, int samples, boolean sw)
    case PIPE_FORMAT_R8G8B8A8_UNORM:
    case PIPE_FORMAT_B8G8R8A8_UNORM:
    case PIPE_FORMAT_A8R8G8B8_UNORM:
+      strb->Base.InternalFormat = GL_RGBA8;
+      break;
    case PIPE_FORMAT_R8G8B8X8_UNORM:
    case PIPE_FORMAT_B8G8R8X8_UNORM:
    case PIPE_FORMAT_X8R8G8B8_UNORM:
+      strb->Base.InternalFormat = GL_RGB8;
+      break;
    case PIPE_FORMAT_B5G5R5A1_UNORM:
+      strb->Base.InternalFormat = GL_RGB5_A1;
+      break;
    case PIPE_FORMAT_B4G4R4A4_UNORM:
+      strb->Base.InternalFormat = GL_RGBA4;
+      break;
    case PIPE_FORMAT_B5G6R5_UNORM:
-      strb->Base.InternalFormat = GL_RGBA;
+      strb->Base.InternalFormat = GL_RGB565;
       break;
    case PIPE_FORMAT_Z16_UNORM:
       strb->Base.InternalFormat = GL_DEPTH_COMPONENT16;
@@ -398,7 +436,7 @@ st_render_texture(struct gl_context *ctx,
 
    pipe_resource_reference( &strb->texture, pt );
 
-   pipe_surface_reference(&strb->surface, NULL);
+   pipe_surface_release(pipe, &strb->surface);
 
    assert(strb->rtt_level <= strb->texture->last_level);
 
@@ -422,6 +460,12 @@ st_render_texture(struct gl_context *ctx,
     * passed to the pipe as a (color/depth) render target.
     */
    st_invalidate_state(ctx, _NEW_BUFFERS);
+
+
+   /* Need to trigger a call to update_framebuffer() since we just
+    * attached a new renderbuffer.
+    */
+   ctx->NewState |= _NEW_BUFFERS;
 }
 
 
@@ -444,6 +488,16 @@ st_finish_render_texture(struct gl_context *ctx,
 }
 
 
+/** Debug helper */
+static void
+st_fbo_invalid(const char *reason)
+{
+   if (MESA_DEBUG_FLAGS & DEBUG_INCOMPLETE_FBO) {
+      _mesa_debug(NULL, "Invalid FBO: %s\n", reason);
+   }
+}
+
+
 /**
  * Validate a renderbuffer attachment for a particular set of bindings.
  */
@@ -456,6 +510,7 @@ st_validate_attachment(struct gl_context *ctx,
    const struct st_texture_object *stObj = st_texture_object(att->Texture);
    enum pipe_format format;
    gl_format texFormat;
+   GLboolean valid;
 
    /* Only validate texture attachments for now, since
     * st_renderbuffer_alloc_storage makes sure that
@@ -479,9 +534,14 @@ st_validate_attachment(struct gl_context *ctx,
       format = st_mesa_format_to_pipe_format(linearFormat);
    }
 
-   return screen->is_format_supported(screen, format,
+   valid = screen->is_format_supported(screen, format,
                                       PIPE_TEXTURE_2D,
                                       stObj->pt->nr_samples, bindings);
+   if (!valid) {
+      st_fbo_invalid("Invalid format");
+   }
+
+   return valid;
 }
 
 
@@ -530,12 +590,14 @@ st_validate_framebuffer(struct gl_context *ctx, struct gl_framebuffer *fb)
          screen->get_param(screen, PIPE_CAP_MIXED_COLORBUFFER_FORMATS) != 0;
 
    if (depth->Type && stencil->Type && depth->Type != stencil->Type) {
+      st_fbo_invalid("Different Depth/Stencil buffer formats");
       fb->_Status = GL_FRAMEBUFFER_UNSUPPORTED_EXT;
       return;
    }
    if (depth->Type == GL_RENDERBUFFER_EXT &&
        stencil->Type == GL_RENDERBUFFER_EXT &&
        depth->Renderbuffer != stencil->Renderbuffer) {
+      st_fbo_invalid("Separate Depth/Stencil buffers");
       fb->_Status = GL_FRAMEBUFFER_UNSUPPORTED_EXT;
       return;
    }
@@ -543,6 +605,7 @@ st_validate_framebuffer(struct gl_context *ctx, struct gl_framebuffer *fb)
        stencil->Type == GL_TEXTURE &&
        depth->Texture != stencil->Texture) {
       fb->_Status = GL_FRAMEBUFFER_UNSUPPORTED_EXT;
+      st_fbo_invalid("Different Depth/Stencil textures");
       return;
    }
 
@@ -585,6 +648,7 @@ st_validate_framebuffer(struct gl_context *ctx, struct gl_framebuffer *fb)
             first_format = format;
          } else if (format != first_format) {
             fb->_Status = GL_FRAMEBUFFER_UNSUPPORTED_EXT;
+            st_fbo_invalid("Mixed color formats");
             return;
          }
       }