<div dir="ltr">On 2 January 2014 03:58, Tapani Pälli <span dir="ltr"><<a href="mailto:tapani.palli@intel.com" target="_blank">tapani.palli@intel.com</a>></span> wrote:<br><div class="gmail_extra"><div class="gmail_quote">
<blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex">+<br>
+<br>
+/**<br>
+ * Reads header part of the binary blob. Main purpose of this header is to<br>
+ * validate that cached shader was produced with same Mesa driver version.<br>
+ */<br>
+int<br>
+ir_deserializer::read_header(struct gl_shader *shader)<br>
+{<br>
+   char *cache_magic_id = map->read_string();<br>
+   char *driver_vendor = map->read_string();<br>
+   char *driver_renderer = map->read_string();<br>
+<br>
+   /* only used or debug output, silence compiler warning */<br>
+   (void) driver_vendor;<br>
+   (void) driver_renderer;<br></blockquote><div><br></div><div>A single version of Mesa potentially supports many different hardware types, and those different hardware types may define different values of GLSL built-in constants.  They also may require core Mesa to do different sets of lowering passes during compilation.  So we can't just ignore driver_vendor and driver_renderer.  We need to reject the binary blob if they don't match. <br>
</div><div> </div><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex">
+<br>
+   shader->Version = map->read_uint32_t();<br>
+   shader->Type = map->read_uint32_t();<br>
+   shader->IsES = map->read_uint8_t();<br>
+<br>
+   CACHE_DEBUG("%s: version %d, type 0x%x, %s (mesa %s)\n[%s %s]\n",<br>
+               __func__,  shader->Version, shader->Type,<br>
+               (shader->IsES) ? "glsl es" : "desktop glsl",<br>
+               cache_magic_id, driver_vendor, driver_renderer);<br>
+<br>
+   const char *magic = mesa_get_shader_cache_magic();<br>
+<br>
+   if (memcmp(cache_magic_id, magic, strlen(magic)))<br>
+      return DIFFERENT_MESA_VER;<br></blockquote><div><br></div><div>If cache_magic_id is "foobar" and magic is "foo", this will erroneusly consider them equal.  The correct way to do this is to use strcmp().<br>
</div><div> </div><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex">
+<br>
+   /* post-link data */<br>
+   shader->num_samplers = map->read_uint32_t();<br>
+   shader->active_samplers = map->read_uint32_t();<br>
+   shader->shadow_samplers = map->read_uint32_t();<br>
+   shader->num_uniform_components = map->read_uint32_t();<br>
+   shader->num_combined_uniform_components = map->read_uint32_t();<br>
+   shader->uses_builtin_functions = map->read_uint8_t();<br>
+<br>
+   map->read(&shader->Geom, sizeof(shader->Geom));<br>
+<br>
+   for (unsigned i = 0; i < MAX_SAMPLERS; i++)<br>
+      shader->SamplerUnits[i] = map->read_uint8_t();<br>
+<br>
+   for (unsigned i = 0; i < MAX_SAMPLERS; i++)<br>
+      shader->SamplerTargets[i] = (gl_texture_index) map->read_int32_t();<br>
+<br>
+   return 0;<br>
+}<br>
+<br>
+<br>
+const glsl_type *<br>
+ir_deserializer::read_glsl_type()<br>
+{<br>
+   char *name = map->read_string();<br>
+   uint32_t type_size = map->read_uint32_t();<br>
+<br>
+   const glsl_type *existing_type =<br>
+      state->symbols->get_type(name);<br>
+<br>
+   /* if type exists, move read pointer forward and return type */<br>
+   if (existing_type) {<br>
+      map->ffwd(type_size);<br>
+      return existing_type;<br>
+   }<br>
+<br>
+   uint8_t base_type = map->read_uint8_t();<br>
+   uint32_t length = map->read_uint32_t();<br>
+   uint8_t vector_elms = map->read_uint8_t();<br>
+   uint8_t matrix_cols = map->read_uint8_t();<br>
+   uint8_t interface_packing = map->read_uint8_t();<br>
+<br>
+   /* array type has additional element_type information */<br>
+   if (base_type == GLSL_TYPE_ARRAY) {<br>
+      const glsl_type *element_type = read_glsl_type();<br>
+      if (!element_type) {<br>
+         CACHE_DEBUG("error reading array element type\n");<br>
+         return NULL;<br>
+      }<br>
+      return glsl_type::get_array_instance(element_type, length);<br>
+   }<br>
+<br>
+   /* structures have fields containing of names and types */<br>
+   else if (base_type == GLSL_TYPE_STRUCT ||<br>
+      base_type == GLSL_TYPE_INTERFACE) {<br>
+      glsl_struct_field *fields = ralloc_array(mem_ctx,<br>
+         glsl_struct_field, length);<br>
+<br>
+      if (!fields)<br>
+         return glsl_type::error_type;<br>
+<br>
+      for (unsigned k = 0; k < length; k++) {<br>
+         uint8_t row_major, interpolation, centroid;<br>
+         int32_t location;<br>
+         char *field_name = map->read_string();<br>
+         fields[k].name = _mesa_strdup(field_name);<br>
+         fields[k].type = read_glsl_type();<br>
+         row_major = map->read_uint8_t();<br>
+         location = map->read_int32_t();<br>
+         interpolation = map->read_uint8_t();<br>
+         centroid = map->read_uint8_t();<br>
+         fields[k].row_major = row_major;<br>
+         fields[k].location = location;<br>
+         fields[k].interpolation = interpolation;<br>
+         fields[k].centroid = centroid;<br></blockquote><div><br></div><div>Another security issue: if the binary blob is corrupted, length may be outrageously large (e.g. 0xffffffff).  We need a way for this loop to bail out and exit if it tries to read past the end of the binary blob.<br>
</div><div> </div><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex">
+      }<br>
+<br>
+      const glsl_type *ret_type = NULL;<br>
+<br>
+      if (base_type == GLSL_TYPE_STRUCT)<br>
+         ret_type = glsl_type::get_record_instance(fields, length, name);<br>
+      else if (base_type == GLSL_TYPE_INTERFACE)<br>
+         ret_type = glsl_type::get_interface_instance(fields,<br>
+            length, (glsl_interface_packing) interface_packing, name);<br>
+<br>
+      /* free allocated memory */<br>
+      for (unsigned k = 0; k < length; k++)<br>
+         free((void *)fields[k].name);<br>
+      ralloc_free(fields);<br>
+<br>
+      return ret_type;<br>
+   }<br>
+<br>
+   return glsl_type::get_instance(base_type, vector_elms, matrix_cols);<br>
+}<br>
+<br>
+<br>
+ir_instruction *<br>
+ir_deserializer::read_ir_variable()<br>
+{<br>
+   const glsl_type *type = read_glsl_type();<br>
+<br>
+   char *name = map->read_string();<br>
+   int64_t unique_id = map->read_int64_t();<br>
+   uint8_t mode = map->read_uint8_t();<br>
+<br>
+   ir_variable *var = new(mem_ctx)ir_variable(type,<br>
+      name, (ir_variable_mode) mode);<br>
+<br>
+   if (!var)<br>
+      return NULL;<br>
+<br>
+   map->read(&var->data, sizeof(var->data));<br>
+<br>
+   var->num_state_slots = map->read_uint32_t();<br>
+   uint8_t has_constant_value = map->read_uint8_t();<br>
+   uint8_t has_constant_initializer = map->read_uint8_t();<br>
+<br>
+   var->state_slots = NULL;<br>
+<br>
+   if (var->num_state_slots > 0) {<br>
+      var->state_slots = ralloc_array(var, ir_state_slot,<br>
+         var->num_state_slots);<br></blockquote><div><br></div><div>Security issue: if the shader blob is corrupted, num_state_slots could be outrageously large, causing this memory allocation to fail (which would then cause the loop below to perform illegal memory accesses).  We should validate that num_state_slots is within a reasonable range, and if it isn't, abort reading the binary blob.<br>
</div><div> </div><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex">
+<br>
+      for (unsigned i = 0; i < var->num_state_slots; i++) {<br>
+         var->state_slots[i].swizzle = map->read_int32_t();<br>
+         for (int j = 0; j < 5; j++) {<br>
+            var->state_slots[i].tokens[j] = map->read_int32_t();<br>
+         }<br>
+      }<br>
+   }<br>
+<br>
+   if (has_constant_value)<br>
+      var->constant_value = read_ir_constant();<br>
+<br>
+   if (has_constant_initializer)<br>
+      var->constant_initializer = read_ir_constant();<br>
+<br>
+   uint8_t has_interface_type = map->read_uint8_t();<br>
+<br>
+   if (has_interface_type)<br>
+      var->init_interface_type(read_glsl_type());<br>
+<br>
+   /**<br>
+    * Store address to this variable so that variable<br>
+    * dereference readers can find it later.<br>
+    */<br>
+   _mesa_hash_table_insert(var_ht, hash_value,<br>
+      (void*) (intptr_t) unique_id, var);<br>
+<br>
+   return var;<br>
+}<br>
+<br>
+<br>
+ir_instruction *<br>
+ir_deserializer::read_ir_function(bool prototypes_only)<br>
+{<br>
+   uint8_t is_builtin = 0;<br>
+   uint8_t ir_type;<br>
+   uint32_t len;<br>
+<br>
+   char *name = map->read_string();<br>
+   uint32_t num_signatures = map->read_uint32_t();<br>
+<br>
+   ir_function *f = new(mem_ctx) ir_function(name);<br>
+   ir_function_signature *sig = NULL;<br>
+<br>
+   /* add all signatures to the function */<br>
+   for (unsigned j = 0; j < num_signatures; j++) {<br></blockquote><div><br></div><div>Security issue: if the blob is corrupted, num_signatures may be outrageously large.  We need a way for this loop to bail out if we try to read past the end of the blob.<br>
</div><div> </div><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex">
+<br>
+      /* ir_function_signature */<br>
+      ir_type = map->read_uint8_t();<br>
+      len = map->read_uint32_t();<br>
+<br>
+      /* used for debugging */<br>
+      (void) ir_type;<br>
+      (void) len;<br></blockquote><div><br></div><div>This is a nice opportunity to do some validation and detect corrupted blobs.  I think we should go ahead and do that.<br></div><div> </div><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex">

+<br>
+      is_builtin = map->read_uint8_t();<br>
+<br>
+      CACHE_DEBUG("%s: [%s] (is_builtin %d)\n", __func__, name, is_builtin);<br>
+<br>
+      const glsl_type *return_type = read_glsl_type();<br>
+<br>
+      if (!return_type) {<br>
+         CACHE_DEBUG("no return type found for [%s]\n", name);<br>
+         return NULL;<br>
+      }<br>
+<br>
+      sig = new(mem_ctx) ir_function_signature(return_type);<br>
+<br>
+      /* fill parameters for function signature */<br>
+      if (deserialize_list(&sig->parameters))<br>
+         goto read_errors;<br>
+<br>
+      /* fill instructions for the function body */<br>
+      if (!prototypes_only) {<br>
+         uint32_t body_count = map->read_uint32_t();<br>
+         for (unsigned k = 0; k < body_count; k++)<br>
+            if (read_instruction(&sig->body, is_builtin ? true : false))<br>
+               goto read_errors;<br>
+         sig->is_defined = body_count ? 1 : 0;<br>
+      }<br>
+<br>
+      if (!is_builtin) {<br>
+         f->add_signature(sig);<br>
+      } else {<br>
+         ir_function_signature *builtin_sig =<br>
+            _mesa_glsl_find_builtin_function(state, name, &sig->parameters);<br>
+<br>
+         if (builtin_sig) {<br>
+            CACHE_DEBUG("found builtin signature for [%s]\n", name);<br>
+            f->add_signature(sig);<br>
+         } else {<br>
+            CACHE_DEBUG("cannot find builtin, function [%s]\n", name);<br>
+            return NULL;<br>
+         }<br>
+      }<br>
+<br>
+   } /* for each function signature */<br>
+<br>
+   CACHE_DEBUG("added %s function [%s]\n",<br>
+               is_builtin ? "builtin" : "user", name);<br>
+<br>
+   return f;<br>
+<br>
+read_errors:<br>
+   CACHE_DEBUG("%s: read errors with [%s]\n", __func__, name);<br>
+   if (sig)<br>
+      ralloc_free(sig);<br>
+   return NULL;<br>
+<br>
+}<br>
+<br>
+<br>
+ir_dereference_array *<br>
+ir_deserializer::read_ir_dereference_array()<br>
+{<br>
+   uint8_t ir_type = map->read_uint8_t();<br>
+   uint32_t len = map->read_uint32_t();<br>
+<br>
+   /* used for debugging */<br>
+   (void) ir_type;<br>
+   (void) len;<br></blockquote><div><br></div><div>We should validate these too.  Similar comments apply throughout this file.<br></div><div> </div><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex">

+<br>
+   CACHE_DEBUG("%s: ir_type %d len %d\n", __func__, ir_type, len);<br>
+<br>
+   ir_rvalue *array_rval = read_ir_rvalue();<br>
+   ir_rvalue *index_rval = read_ir_rvalue();<br>
+<br>
+   if (array_rval && index_rval)<br>
+      return new(mem_ctx) ir_dereference_array(array_rval, index_rval);<br>
+<br>
+   CACHE_DEBUG("%s: could not get rvalues", __func__);<br>
+   return NULL;<br>
+}<br>
+<br>
+<br>
+<br>
+<br>
+ir_constant *<br>
+ir_deserializer::read_ir_constant()<br>
+{<br>
+   ir_constant *con = NULL;<br>
+   uint8_t ir_type = map->read_uint8_t();<br>
+   uint32_t len = map->read_uint32_t();<br>
+<br>
+   /* used for debugging */<br>
+   (void) ir_type;<br>
+   (void) len;<br>
+<br>
+   const glsl_type *constant_type = read_glsl_type();<br>
+<br>
+   /* data structure */<br>
+   ir_constant_data data;<br>
+   map->read(&data, sizeof(data));<br>
+<br>
+   con = new(mem_ctx) ir_constant(constant_type, &data);<br>
+<br>
+   /* constant with array of constants */<br>
+   if (constant_type->base_type == GLSL_TYPE_ARRAY) {<br>
+      con->array_elements = ralloc_array(mem_ctx, ir_constant *,<br>
+         constant_type->length);<br>
+<br>
+      for (unsigned i = 0; i < constant_type->length; i++)<br>
+         con->array_elements[i] = read_ir_constant();<br></blockquote><div><br></div><div>Security issue: if the blob is corrupted, constant_type->length might be outrageously large.  We need a way for this loop to terminate if we try to read past the end of the blob.<br>
</div><div> </div><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex">
+<br>
+   } else if (constant_type->base_type == GLSL_TYPE_STRUCT) {<br>
+      if (deserialize_list(&con->components)) {<br>
+         ralloc_free(con);<br>
+         return NULL;<br>
+      }<br>
+   }<br>
+<br>
+   return con;<br>
+}<br>
+<br>
+<br>+<br>
+<br>
+/**<br>
+ * Go through the blob and read prototypes for the functions<br>
+ */<br>
+int<br>
+ir_deserializer::read_prototypes(unsigned list_len)<br>
+{<br>
+   uint32_t ir_start = map->position();<br>
+<br>
+   for (unsigned k = 0; k < list_len; k++) {<br></blockquote><div><br></div><div>Security issue: we need a way for this loop to exit if we try to read beyond the end of the blob.<br></div><div> </div><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex">

+      /* peek type of next instruction */<br></blockquote><div><br></div><div>"peek" implies that you're going to look at the data but not advance the read pointer.  But it looks like you're advancing the read pointer anyhow.  Which is correct, the code or the comment?<br>
</div><div> </div><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex">
+      uint8_t ir_type = map->read_uint8_t();<br>
+      uint32_t len = map->read_uint32_t();<br>
+<br>
+      /* ignore if not ir_function */<br>
+      if (ir_type != ir_type_function) {<br>
+         map->ffwd(len);<br>
+         continue;<br>
+      }<br>
+<br>
+      ir_instruction *func = read_ir_function(true);<br>
+      if (!func)<br>
+         return -1;<br>
+<br>
+      prototypes->push_tail(func);<br>
+   }<br>
+<br>
+   /* go back to beginning */<br>
+   map->jump(ir_start);<br>
+   return 0;<br>
+}<br>
+<br>
+<br>
+struct gl_shader *<br>
+ir_deserializer::deserialize(void *mem_ctx, memory_map *map,<br>
+   uint32_t shader_size, int *error_code)<br>
+{<br>
+   int error = 0;<br>
+<br>
+   *error_code = ir_deserializer::GENERAL_READ_ERROR;<br>
+<br>
+   this->map = map;<br>
+<br>
+   uint32_t type = map->read_uint32_t();<br>
+   uint32_t exec_list_len;<br>
+<br>
+   GET_CURRENT_CONTEXT(ctx);<br>
+   struct gl_shader *shader = ctx->Driver.NewShader(NULL, 0, type);<br>
+<br>
+   if (!shader)<br>
+      return NULL;<br>
+<br>
+   shader->Source = NULL;<br>
+   shader->Label = NULL;<br>
+   shader->InfoLog = ralloc_strdup(mem_ctx, "");<br>
+   shader->ir = NULL;<br>
+<br>
+   if (read_header(shader)) {<br>
+      *error_code = ir_deserializer::DIFFERENT_MESA_VER;<br>
+      goto error_deserialize;<br>
+   }<br>
+<br>
+   /**<br>
+    * parse state is used to find builtin functions and<br>
+    * existing types during reading<br>
+    */<br>
+   state = new(mem_ctx) _mesa_glsl_parse_state(ctx, shader->Type, shader);<br>
+<br>
+   /* fill parse state from shader header information */<br>
+   switch (shader->Type) {<br>
+   case GL_VERTEX_SHADER:<br>
+      state->target = MESA_SHADER_VERTEX;<br>
+      break;<br>
+   case GL_FRAGMENT_SHADER:<br>
+      state->target = MESA_SHADER_FRAGMENT;<br>
+      break;<br>
+   case GL_GEOMETRY_SHADER_ARB:<br>
+      state->target = MESA_SHADER_GEOMETRY;<br>
+      break;<br></blockquote><div><br></div><div>There should be a default case here to handle corrupted blobs with an illegal type.<br></div><div> </div><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex">

+   }<br>
+<br>
+   state->uses_builtin_functions = true;<br>
+   _mesa_glsl_initialize_builtin_functions();<br>
+   _mesa_glsl_initialize_types(state);<br>
+<br>
+   /* allocations during reading */<br>
+   this->mem_ctx = mem_ctx;<br>
+<br>
+   prototypes = new(mem_ctx) exec_list;<br>
+   shader->ir = new(shader) exec_list;<br>
+<br>
+   exec_list_len = map->read_uint32_t();<br>
+<br>
+   error = read_prototypes(exec_list_len);<br>
+<br>
+   CACHE_DEBUG("reading %d IR instructions\n", exec_list_len);<br>
+<br>
+   /* top level exec_list read loop, constructs a new list */<br>
+   while(map->position() < shader_size && error == 0)<br>
+      error = read_instruction(shader->ir);<br>
+<br>
+   ralloc_free(prototypes);<br>
+<br>
+   if (error)<br>
+      goto error_deserialize;<br>
+<br>
+   *error_code = 0;<br>
+<br>
+   shader->CompileStatus = GL_TRUE;<br>
+<br>
+   /* allocates glsl_symbol_table internally */<br>
+   populate_symbol_table(shader);<br>
+<br>
+   validate_ir_tree(shader->ir);<br>
+<br>
+   CACHE_DEBUG("shader from cache\n");<br>
+<br>
+   return shader;<br>
+<br>
+error_deserialize:<br>
+   ralloc_free(shader);<br>
+   return NULL;<br>
+}<br></blockquote></div></div></div>