Commit message: vrend: fix memory leak in int blit context

The 'blit_ctx->initialised' is not setted to true.
Every time init blit context, it will create a new
'blit_ctx->gl_context' thus causing a memory leak.
This patch avoid this.

Signed-off-by: Li Qiang <liq3ea@gmail.com>
Signed-off-by: Dave Airlie <airlied@redhat.com>
CVE: CVE-2017-5993
CWE: CWE-772
Vulnerability description: Memory leak in the vrend_renderer_init_blit_ctx function in vrend_blitter.c in virglrenderer before 0.6.0 allows local guest OS users to cause a denial of service (host memory consumption) via a large number of VIRGL_CCMD_BLIT commands.

Project: virglrenderer
File: vrend_blitter.c

static GLuint blit_build_frag_blit_msaa_depth(struct vrend_blitter_ctx *blit_ctx, int tgsi_tex_target)
{
   GLuint fs_id;
   char shader_buf[4096];
   int is_shad;
   const char *twm;
   const char *ivec;

   switch (tgsi_tex_target) {
   case TGSI_TEXTURE_2D_MSAA:
      twm = ".xy";
      ivec = "ivec2";
      break;
   case TGSI_TEXTURE_2D_ARRAY_MSAA:
      twm = ".xyz";
      ivec = "ivec3";
      break;
   default:
      return 0;
   }

   snprintf(shader_buf, 4096, FS_TEXFETCH_DS_MSAA, vrend_shader_samplertypeconv(tgsi_tex_target, &is_shad), ivec, twm);

   fs_id = glCreateShader(GL_FRAGMENT_SHADER);

   if (!build_and_check(fs_id, shader_buf)) {
      glDeleteShader(fs_id);
      return 0;
   }

   return fs_id;
}

Project: virglrenderer
File: vrend_blitter.c

static GLuint blit_build_frag_tex_col_emu_alpha(struct vrend_blitter_ctx *blit_ctx, int tgsi_tex_target)
{
   GLuint fs_id;
   char shader_buf[4096];
   int is_shad;
   const char *twm;
   const char *ext_str = "";
   switch (tgsi_tex_target) {
   case TGSI_TEXTURE_1D:
   case TGSI_TEXTURE_BUFFER:
      twm = ".x";
      break;
   case TGSI_TEXTURE_1D_ARRAY:
   case TGSI_TEXTURE_2D:
   case TGSI_TEXTURE_RECT:
   case TGSI_TEXTURE_2D_MSAA:
   default:
      twm = ".xy";
      break;
   case TGSI_TEXTURE_SHADOW1D:
   case TGSI_TEXTURE_SHADOW2D:
   case TGSI_TEXTURE_SHADOW1D_ARRAY:
   case TGSI_TEXTURE_SHADOWRECT:
   case TGSI_TEXTURE_3D:
   case TGSI_TEXTURE_CUBE:
   case TGSI_TEXTURE_2D_ARRAY:
   case TGSI_TEXTURE_2D_ARRAY_MSAA:
      twm = ".xyz";
      break;
   case TGSI_TEXTURE_SHADOWCUBE:
   case TGSI_TEXTURE_SHADOW2D_ARRAY:
   case TGSI_TEXTURE_SHADOWCUBE_ARRAY:
   case TGSI_TEXTURE_CUBE_ARRAY:
      twm = "";
      break;
   }

   if (tgsi_tex_target == TGSI_TEXTURE_CUBE_ARRAY ||
       tgsi_tex_target == TGSI_TEXTURE_SHADOWCUBE_ARRAY)
      ext_str = "#extension GL_ARB_texture_cube_map_array : require\n";

   snprintf(shader_buf, 4096, FS_TEXFETCH_COL_ALPHA_DEST, ext_str, vrend_shader_samplertypeconv(tgsi_tex_target, &is_shad), twm, "");

   fs_id = glCreateShader(GL_FRAGMENT_SHADER);

   if (!build_and_check(fs_id, shader_buf)) {
      glDeleteShader(fs_id);
      return 0;
   }

   return fs_id;
}

Project: virglrenderer
File: vrend_blitter.c

static GLuint blit_build_frag_tex_writedepth(struct vrend_blitter_ctx *blit_ctx, int tgsi_tex_target)
{
   GLuint fs_id;
   char shader_buf[4096];
   int is_shad;
   const char *twm;

   switch (tgsi_tex_target) {
   case TGSI_TEXTURE_1D:
   case TGSI_TEXTURE_BUFFER:
      twm = ".x";
      break;
   case TGSI_TEXTURE_1D_ARRAY:
   case TGSI_TEXTURE_2D:
   case TGSI_TEXTURE_RECT:
   case TGSI_TEXTURE_2D_MSAA:
   default:
      twm = ".xy";
      break;
   case TGSI_TEXTURE_SHADOW1D:
   case TGSI_TEXTURE_SHADOW2D:
   case TGSI_TEXTURE_SHADOW1D_ARRAY:
   case TGSI_TEXTURE_SHADOWRECT:
   case TGSI_TEXTURE_3D:
   case TGSI_TEXTURE_CUBE:
   case TGSI_TEXTURE_2D_ARRAY:
   case TGSI_TEXTURE_2D_ARRAY_MSAA:
      twm = ".xyz";
      break;
   case TGSI_TEXTURE_SHADOWCUBE:
   case TGSI_TEXTURE_SHADOW2D_ARRAY:
   case TGSI_TEXTURE_SHADOWCUBE_ARRAY:
   case TGSI_TEXTURE_CUBE_ARRAY:
      twm = "";
      break;
   }

   snprintf(shader_buf, 4096, FS_TEXFETCH_DS, vrend_shader_samplertypeconv(tgsi_tex_target, &is_shad), twm);

   fs_id = glCreateShader(GL_FRAGMENT_SHADER);

   if (!build_and_check(fs_id, shader_buf)) {
      glDeleteShader(fs_id);
      return 0;
   }

   return fs_id;
}

Project: virglrenderer
File: vrend_blitter.c

static bool blit_build_vs_passthrough(struct vrend_blitter_ctx *blit_ctx)
{
   blit_ctx->vs = glCreateShader(GL_VERTEX_SHADER);

   if (!build_and_check(blit_ctx->vs, VS_PASSTHROUGH)) {
      glDeleteShader(blit_ctx->vs);
      blit_ctx->vs = 0;
      return false;
   }
   return true;
}

Project: virglrenderer
File: vrend_blitter.c

static GLuint blit_get_frag_tex_col_emu_alpha(struct vrend_blitter_ctx *blit_ctx, int pipe_tex_target, unsigned nr_samples)
{
   assert(pipe_tex_target < PIPE_MAX_TEXTURE_TYPES);

   if (nr_samples > 1) {
      return 0;
   } else {
      GLuint *shader = &blit_ctx->fs_texfetch_col_emu_alpha[pipe_tex_target];

      if (!*shader) {
         unsigned tgsi_tex = util_pipe_tex_to_tgsi_tex(pipe_tex_target, 0);

         *shader = blit_build_frag_tex_col_emu_alpha(blit_ctx, tgsi_tex);
      }
      return *shader;
   }
}

Project: virglrenderer
File: vrend_blitter.c

static GLuint blit_get_frag_tex_writedepth(struct vrend_blitter_ctx *blit_ctx, int pipe_tex_target, unsigned nr_samples)
{
   assert(pipe_tex_target < PIPE_MAX_TEXTURE_TYPES);

   if (nr_samples > 1) {
      GLuint *shader = &blit_ctx->fs_texfetch_depth_msaa[pipe_tex_target];

      if (!*shader) {
         unsigned tgsi_tex = util_pipe_tex_to_tgsi_tex(pipe_tex_target, nr_samples);

         *shader = blit_build_frag_blit_msaa_depth(blit_ctx, tgsi_tex);
      }
      return *shader;

   } else {
      GLuint *shader = &blit_ctx->fs_texfetch_depth[pipe_tex_target];

      if (!*shader) {
         unsigned tgsi_tex = util_pipe_tex_to_tgsi_tex(pipe_tex_target, 0);

         *shader = blit_build_frag_tex_writedepth(blit_ctx, tgsi_tex);
      }
      return *shader;
   }
}

Project: virglrenderer
File: vrend_blitter.c

static void blitter_set_dst_dim(struct vrend_blitter_ctx *blit_ctx,
                                unsigned width, unsigned height)
{
   blit_ctx->dst_width = width;
   blit_ctx->dst_height = height;
}

Project: virglrenderer
File: vrend_blitter.c

static void blitter_set_rectangle(struct vrend_blitter_ctx *blit_ctx,
                                  int x1, int y1, int x2, int y2,
                                  float depth)
{
   int i;

   /* set vertex positions */
   blit_ctx->vertices[0][0][0] = (float)x1 / blit_ctx->dst_width * 2.0f - 1.0f; /*v0.x*/
   blit_ctx->vertices[0][0][1] = (float)y1 / blit_ctx->dst_height * 2.0f - 1.0f; /*v0.y*/

   blit_ctx->vertices[1][0][0] = (float)x2 / blit_ctx->dst_width * 2.0f - 1.0f; /*v1.x*/
   blit_ctx->vertices[1][0][1] = (float)y1 / blit_ctx->dst_height * 2.0f - 1.0f; /*v1.y*/

   blit_ctx->vertices[2][0][0] = (float)x2 / blit_ctx->dst_width * 2.0f - 1.0f; /*v2.x*/
   blit_ctx->vertices[2][0][1] = (float)y2 / blit_ctx->dst_height * 2.0f - 1.0f; /*v2.y*/

   blit_ctx->vertices[3][0][0] = (float)x1 / blit_ctx->dst_width * 2.0f - 1.0f; /*v3.x*/
   blit_ctx->vertices[3][0][1] = (float)y2 / blit_ctx->dst_height * 2.0f - 1.0f; /*v3.y*/

   for (i = 0; i < 4; i++)
      blit_ctx->vertices[i][0][2] = depth; /*z*/

   glViewport(0, 0, blit_ctx->dst_width, blit_ctx->dst_height);
}

Project: virglrenderer
File: vrend_blitter.c

static void blitter_set_texcoords(struct vrend_blitter_ctx *blit_ctx,
                                  struct vrend_resource *src_res,
                                  int level,
                                  float layer, unsigned sample,
                                  int x1, int y1, int x2, int y2)
{
   float coord[4];
   float face_coord[4][2];
   int i;
   get_texcoords(src_res, level, x1, y1, x2, y2, coord);

   if (src_res->base.target == PIPE_TEXTURE_CUBE ||
       src_res->base.target == PIPE_TEXTURE_CUBE_ARRAY) {
      set_texcoords_in_vertices(coord, &face_coord[0][0], 2);
      util_map_texcoords2d_onto_cubemap((unsigned)layer % 6,
                                        /* pointer, stride in floats */
                                        &face_coord[0][0], 2,
                                        &blit_ctx->vertices[0][1][0], 8,
                                        FALSE);
   } else {
      set_texcoords_in_vertices(coord, &blit_ctx->vertices[0][1][0], 8);
   }

   switch (src_res->base.target) {
   case PIPE_TEXTURE_3D:
   {
      float r = layer / (float)u_minify(src_res->base.depth0,
                                        level);
      for (i = 0; i < 4; i++)
         blit_ctx->vertices[i][1][2] = r; /*r*/
   }
   break;

   case PIPE_TEXTURE_1D_ARRAY:
      for (i = 0; i < 4; i++)
         blit_ctx->vertices[i][1][1] = (float) layer; /*t*/
      break;

   case PIPE_TEXTURE_2D_ARRAY:
      for (i = 0; i < 4; i++) {
         blit_ctx->vertices[i][1][2] = (float) layer;  /*r*/
         blit_ctx->vertices[i][1][3] = (float) sample; /*q*/
      }
      break;
   case PIPE_TEXTURE_CUBE_ARRAY:
      for (i = 0; i < 4; i++)
         blit_ctx->vertices[i][1][3] = (float) ((unsigned)layer / 6); /*w*/
      break;
   case PIPE_TEXTURE_2D:
      for (i = 0; i < 4; i++) {
         blit_ctx->vertices[i][1][3] = (float) sample; /*r*/
      }
      break;
   default:;
   }
}

Project: virglrenderer
File: vrend_blitter.c

static bool build_and_check(GLuint id, const char *buf)
{
   GLint param;
   glShaderSource(id, 1, (const char **)&buf, NULL);
   glCompileShader(id);

   glGetShaderiv(id, GL_COMPILE_STATUS, &param);
   if (param == GL_FALSE) {
      char infolog[65536];
      int len;
      glGetShaderInfoLog(id, 65536, &len, infolog);
      fprintf(stderr,"shader failed to compile\n%s\n", infolog);
      fprintf(stderr,"GLSL:\n%s\n", buf);
      return false;
   }
   return true;
}

Project: virglrenderer
File: vrend_blitter.c

static void get_texcoords(struct vrend_resource *src_res,
                          int src_level,
                          int x1, int y1, int x2, int y2,
                          float out[4])
{
   bool normalized = src_res->base.target != PIPE_TEXTURE_RECT &&
      src_res->base.nr_samples <= 1;

   if (normalized) {
      out[0] = x1 / (float)u_minify(src_res->base.width0,  src_level);
      out[1] = y1 / (float)u_minify(src_res->base.height0, src_level);
      out[2] = x2 / (float)u_minify(src_res->base.width0,  src_level);
      out[3] = y2 / (float)u_minify(src_res->base.height0, src_level);
   } else {
      out[0] = (float) x1;
      out[1] = (float) y1;
      out[2] = (float) x2;
      out[3] = (float) y2;
   }
}

Project: virglrenderer
File: vrend_blitter.c

static void set_dsa_write_depth_keep_stencil(void)
{
   glDisable(GL_STENCIL_TEST);
   glEnable(GL_DEPTH_TEST);
   glDepthFunc(GL_ALWAYS);
   glDepthMask(GL_TRUE);
}

Project: virglrenderer
File: vrend_blitter.c

static void set_texcoords_in_vertices(const float coord[4],
                                      float *out, unsigned stride)
{
   out[0] = coord[0]; /*t0.s*/
   out[1] = coord[1]; /*t0.t*/
   out += stride;
   out[0] = coord[2]; /*t1.s*/
   out[1] = coord[1]; /*t1.t*/
   out += stride;
   out[0] = coord[2]; /*t2.s*/
   out[1] = coord[3]; /*t2.t*/
   out += stride;
   out[0] = coord[0]; /*t3.s*/
   out[1] = coord[3]; /*t3.t*/
}

Project: virglrenderer
File: vrend_blitter.c

void vrend_renderer_blit_gl(struct vrend_context *ctx,
                            struct vrend_resource *src_res,
                            struct vrend_resource *dst_res,
                            const struct pipe_blit_info *info)
{
   struct vrend_blitter_ctx *blit_ctx = &vrend_blit_ctx;
   GLuint prog_id;
   GLuint fs_id;
   GLint lret;
   GLenum filter;
   GLuint pos_loc, tc_loc;
   GLuint samp_loc;
   bool has_depth, has_stencil;
   bool blit_stencil, blit_depth;
   int dst_z;
   const struct util_format_description *src_desc =
      util_format_description(src_res->base.format);
   const struct util_format_description *dst_desc =
      util_format_description(dst_res->base.format);

   has_depth = util_format_has_depth(src_desc) &&
      util_format_has_depth(dst_desc);
   has_stencil = util_format_has_stencil(src_desc) &&
      util_format_has_stencil(dst_desc);

   blit_depth = has_depth && (info->mask & PIPE_MASK_Z);
   blit_stencil = has_stencil && (info->mask & PIPE_MASK_S) & 0;

   filter = convert_mag_filter(info->filter);
   vrend_renderer_init_blit_ctx(blit_ctx);

   blitter_set_dst_dim(blit_ctx,
                       u_minify(dst_res->base.width0, info->dst.level),
                       u_minify(dst_res->base.height0, info->dst.level));

   blitter_set_rectangle(blit_ctx, info->dst.box.x, info->dst.box.y,
                         info->dst.box.x + info->dst.box.width,
                         info->dst.box.y + info->dst.box.height, 0);


   prog_id = glCreateProgram();
   glAttachShader(prog_id, blit_ctx->vs);

   if (blit_depth || blit_stencil)
      fs_id = blit_get_frag_tex_writedepth(blit_ctx, src_res->base.target, src_res->base.nr_samples);
   else if (vrend_format_is_emulated_alpha(info->dst.format))
      fs_id = blit_get_frag_tex_col_emu_alpha(blit_ctx, src_res->base.target, src_res->base.nr_samples);
   else
      fs_id = blit_get_frag_tex_col(blit_ctx, src_res->base.target, src_res->base.nr_samples);
   glAttachShader(prog_id, fs_id);

   glLinkProgram(prog_id);
   glGetProgramiv(prog_id, GL_LINK_STATUS, &lret);
   if (lret == GL_FALSE) {
      char infolog[65536];
      int len;
      glGetProgramInfoLog(prog_id, 65536, &len, infolog);
      fprintf(stderr,"got error linking\n%s\n", infolog);
      /* dump shaders */
      glDeleteProgram(prog_id);
      return;
   }

   glUseProgram(prog_id);

   glBindFramebuffer(GL_FRAMEBUFFER_EXT, blit_ctx->fb_id);
   vrend_fb_bind_texture(dst_res, 0, info->dst.level, info->dst.box.z);

   glDrawBuffer(GL_COLOR_ATTACHMENT0_EXT);

   glBindTexture(src_res->target, src_res->id);

   if (vrend_format_is_emulated_alpha(info->src.format))
      glTexParameteri(src_res->target, GL_TEXTURE_SWIZZLE_R, GL_ALPHA);

   glTexParameteri(src_res->target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
   glTexParameteri(src_res->target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
   glTexParameteri(src_res->target, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE);

   glTexParameteri(src_res->target, GL_TEXTURE_BASE_LEVEL, info->src.level);
   glTexParameteri(src_res->target, GL_TEXTURE_MAX_LEVEL, info->src.level);
   glTexParameterf(src_res->target, GL_TEXTURE_MAG_FILTER, filter);
   glTexParameterf(src_res->target, GL_TEXTURE_MIN_FILTER, filter);
   pos_loc = glGetAttribLocation(prog_id, "arg0");
   tc_loc = glGetAttribLocation(prog_id, "arg1");
   samp_loc = glGetUniformLocation(prog_id, "samp");

   glUniform1i(samp_loc, 0);

   glVertexAttribPointer(pos_loc, 4, GL_FLOAT, GL_FALSE, 8 * sizeof(float), (void *)0);
   glVertexAttribPointer(tc_loc, 4, GL_FLOAT, GL_FALSE, 8 * sizeof(float), (void *)(4 * sizeof(float)));

   glEnableVertexAttribArray(pos_loc);
   glEnableVertexAttribArray(tc_loc);

   set_dsa_write_depth_keep_stencil();

   for (dst_z = 0; dst_z < info->dst.box.depth; dst_z++) {
      float dst2src_scale = info->src.box.depth / (float)info->dst.box.depth;
      float dst_offset = ((info->src.box.depth - 1) -
                          (info->dst.box.depth - 1) * dst2src_scale) * 0.5;
      float src_z = (dst_z + dst_offset) * dst2src_scale;

      glBindFramebuffer(GL_FRAMEBUFFER_EXT, blit_ctx->fb_id);
      vrend_fb_bind_texture(dst_res, 0, info->dst.level, dst_z);

      glDrawBuffer(GL_COLOR_ATTACHMENT0_EXT);
      blitter_set_texcoords(blit_ctx, src_res, info->src.level,
                            info->src.box.z + src_z, 0,
                            info->src.box.x, info->src.box.y,
                            info->src.box.x + info->src.box.width,
                            info->src.box.y + info->src.box.height);

      glBufferData(GL_ARRAY_BUFFER, sizeof(blit_ctx->vertices), blit_ctx->vertices, GL_STATIC_DRAW);
      glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
   }
}