[PATCH vkd3d 1/5] vkd3d-shader: Validate control flow instructions in vkd3d_shader_scan_dxbc().

Henri Verbeet hverbeet at codeweavers.com
Thu Jul 30 05:29:54 CDT 2020


Signed-off-by: Henri Verbeet <hverbeet at codeweavers.com>
---
 libs/vkd3d-shader/vkd3d_shader_main.c    | 276 +++++++++++++++++++++++++++++--
 libs/vkd3d-shader/vkd3d_shader_private.h |   1 +
 2 files changed, 259 insertions(+), 18 deletions(-)

diff --git a/libs/vkd3d-shader/vkd3d_shader_main.c b/libs/vkd3d-shader/vkd3d_shader_main.c
index 5597992..cf8b289 100644
--- a/libs/vkd3d-shader/vkd3d_shader_main.c
+++ b/libs/vkd3d-shader/vkd3d_shader_main.c
@@ -359,6 +359,22 @@ struct vkd3d_shader_scan_context
     struct vkd3d_shader_scan_info *scan_info;
     size_t descriptors_size;
 
+    struct vkd3d_shader_message_context message_context;
+
+    struct vkd3d_shader_cf_info
+    {
+        enum
+        {
+            VKD3D_SHADER_BLOCK_IF,
+            VKD3D_SHADER_BLOCK_LOOP,
+            VKD3D_SHADER_BLOCK_SWITCH,
+        } type;
+        bool inside_block;
+        bool has_default;
+    } *cf_info;
+    size_t cf_info_size;
+    size_t cf_info_count;
+
     struct
     {
         unsigned int id;
@@ -368,6 +384,85 @@ struct vkd3d_shader_scan_context
     size_t uav_range_count;
 };
 
+static bool vkd3d_shader_scan_context_init(struct vkd3d_shader_scan_context *context,
+        struct vkd3d_shader_scan_info *scan_info)
+{
+    memset(context, 0, sizeof(*context));
+    context->scan_info = scan_info;
+    return vkd3d_shader_message_context_init(&context->message_context, VKD3D_SHADER_LOG_INFO, NULL);
+}
+
+static void vkd3d_shader_scan_context_cleanup(struct vkd3d_shader_scan_context *context)
+{
+    vkd3d_free(context->uav_ranges);
+    vkd3d_free(context->cf_info);
+    vkd3d_shader_message_context_cleanup(&context->message_context);
+}
+
+static struct vkd3d_shader_cf_info *vkd3d_shader_scan_get_current_cf_info(struct vkd3d_shader_scan_context *context)
+{
+    if (!context->cf_info_count)
+        return NULL;
+    return &context->cf_info[context->cf_info_count - 1];
+}
+
+static struct vkd3d_shader_cf_info *vkd3d_shader_scan_push_cf_info(struct vkd3d_shader_scan_context *context)
+{
+    struct vkd3d_shader_cf_info *cf_info;
+
+    if (!vkd3d_array_reserve((void **)&context->cf_info, &context->cf_info_size,
+            context->cf_info_count + 1, sizeof(*context->cf_info)))
+    {
+        ERR("Failed to allocate UAV range.\n");
+        return false;
+    }
+
+    cf_info = &context->cf_info[context->cf_info_count++];
+    memset(cf_info, 0, sizeof(*cf_info));
+
+    return cf_info;
+}
+
+static void vkd3d_shader_scan_pop_cf_info(struct vkd3d_shader_scan_context *context)
+{
+    assert(context->cf_info_count);
+
+    --context->cf_info_count;
+}
+
+static struct vkd3d_shader_cf_info *vkd3d_shader_scan_find_innermost_breakable_cf_info(
+        struct vkd3d_shader_scan_context *context)
+{
+    size_t count = context->cf_info_count;
+    struct vkd3d_shader_cf_info *cf_info;
+
+    while (count)
+    {
+        cf_info = &context->cf_info[--count];
+        if (cf_info->type == VKD3D_SHADER_BLOCK_LOOP
+                || cf_info->type == VKD3D_SHADER_BLOCK_SWITCH)
+            return cf_info;
+    }
+
+    return NULL;
+}
+
+static struct vkd3d_shader_cf_info *vkd3d_shader_scan_find_innermost_loop_cf_info(
+        struct vkd3d_shader_scan_context *context)
+{
+    size_t count = context->cf_info_count;
+    struct vkd3d_shader_cf_info *cf_info;
+
+    while (count)
+    {
+        cf_info = &context->cf_info[--count];
+        if (cf_info->type == VKD3D_SHADER_BLOCK_LOOP)
+            return cf_info;
+    }
+
+    return NULL;
+}
+
 static struct vkd3d_shader_descriptor_info *vkd3d_shader_scan_get_uav_descriptor_info(
         const struct vkd3d_shader_scan_context *context, unsigned int range_id)
 {
@@ -397,6 +492,9 @@ static void vkd3d_shader_scan_record_uav_read(struct vkd3d_shader_scan_context *
 {
     struct vkd3d_shader_descriptor_info *d;
 
+    if (!context->scan_info)
+        return;
+
     d = vkd3d_shader_scan_get_uav_descriptor_info(context, reg->idx[0].offset);
     d->flags |= VKD3D_SHADER_DESCRIPTOR_INFO_FLAG_UAV_READ;
 }
@@ -413,6 +511,9 @@ static void vkd3d_shader_scan_record_uav_counter(struct vkd3d_shader_scan_contex
 {
     struct vkd3d_shader_descriptor_info *d;
 
+    if (!context->scan_info)
+        return;
+
     d = vkd3d_shader_scan_get_uav_descriptor_info(context, reg->idx[0].offset);
     d->flags |= VKD3D_SHADER_DESCRIPTOR_INFO_FLAG_UAV_COUNTER;
 }
@@ -467,6 +568,9 @@ static void vkd3d_shader_scan_constant_buffer_declaration(struct vkd3d_shader_sc
 {
     const struct vkd3d_shader_constant_buffer *cb = &instruction->declaration.cb;
 
+    if (!context->scan_info)
+        return;
+
     vkd3d_shader_scan_add_descriptor(context, VKD3D_SHADER_DESCRIPTOR_TYPE_CBV, cb->register_space,
             cb->register_index, VKD3D_SHADER_RESOURCE_BUFFER, VKD3D_SHADER_RESOURCE_DATA_UINT, 0);
 }
@@ -477,6 +581,9 @@ static void vkd3d_shader_scan_sampler_declaration(struct vkd3d_shader_scan_conte
     const struct vkd3d_shader_sampler *sampler = &instruction->declaration.sampler;
     unsigned int flags;
 
+    if (!context->scan_info)
+        return;
+
     if (instruction->flags & VKD3DSI_SAMPLER_COMPARISON_MODE)
         flags = VKD3D_SHADER_DESCRIPTOR_INFO_FLAG_SAMPLER_COMPARISON_MODE;
     else
@@ -491,6 +598,9 @@ static void vkd3d_shader_scan_resource_declaration(struct vkd3d_shader_scan_cont
 {
     enum vkd3d_shader_descriptor_type type;
 
+    if (!context->scan_info)
+        return;
+
     if (resource->reg.reg.type == VKD3DSPR_UAV)
         type = VKD3D_SHADER_DESCRIPTOR_TYPE_UAV;
     else
@@ -534,9 +644,10 @@ static void vkd3d_shader_scan_typed_resource_declaration(struct vkd3d_shader_sca
             semantic->resource_type, resource_data_type);
 }
 
-static void vkd3d_shader_scan_instruction(struct vkd3d_shader_scan_context *context,
+static int vkd3d_shader_scan_instruction(struct vkd3d_shader_scan_context *context,
         const struct vkd3d_shader_instruction *instruction)
 {
+    struct vkd3d_shader_cf_info *cf_info;
     unsigned int i;
 
     switch (instruction->handler_idx)
@@ -561,6 +672,121 @@ static void vkd3d_shader_scan_instruction(struct vkd3d_shader_scan_context *cont
             vkd3d_shader_scan_resource_declaration(context, &instruction->declaration.structured_resource.resource,
                     VKD3D_SHADER_RESOURCE_BUFFER, VKD3D_SHADER_RESOURCE_DATA_UINT);
             break;
+        case VKD3DSIH_IF:
+            cf_info = vkd3d_shader_scan_push_cf_info(context);
+            cf_info->type = VKD3D_SHADER_BLOCK_IF;
+            cf_info->inside_block = true;
+            break;
+        case VKD3DSIH_ELSE:
+            if (!(cf_info = vkd3d_shader_scan_get_current_cf_info(context)) || cf_info->type != VKD3D_SHADER_BLOCK_IF)
+            {
+                vkd3d_shader_error(&context->message_context, VKD3D_SHADER_ERROR_TPF_MISMATCHED_CF,
+                        "Encountered ‘else’ instruction without corresponding ‘if’ block.");
+                return VKD3D_ERROR_INVALID_SHADER;
+            }
+            cf_info->inside_block = true;
+            break;
+        case VKD3DSIH_ENDIF:
+            if (!(cf_info = vkd3d_shader_scan_get_current_cf_info(context)) || cf_info->type != VKD3D_SHADER_BLOCK_IF)
+            {
+                vkd3d_shader_error(&context->message_context, VKD3D_SHADER_ERROR_TPF_MISMATCHED_CF,
+                        "Encountered ‘endif’ instruction without corresponding ‘if’ block.");
+                return VKD3D_ERROR_INVALID_SHADER;
+            }
+            vkd3d_shader_scan_pop_cf_info(context);
+            break;
+        case VKD3DSIH_LOOP:
+            cf_info = vkd3d_shader_scan_push_cf_info(context);
+            cf_info->type = VKD3D_SHADER_BLOCK_LOOP;
+            break;
+        case VKD3DSIH_ENDLOOP:
+            if (!(cf_info = vkd3d_shader_scan_get_current_cf_info(context)) || cf_info->type != VKD3D_SHADER_BLOCK_LOOP)
+            {
+                vkd3d_shader_error(&context->message_context, VKD3D_SHADER_ERROR_TPF_MISMATCHED_CF,
+                        "Encountered ‘endloop’ instruction without corresponding ‘loop’ block.");
+                return VKD3D_ERROR_INVALID_SHADER;
+            }
+            vkd3d_shader_scan_pop_cf_info(context);
+            break;
+        case VKD3DSIH_SWITCH:
+            cf_info = vkd3d_shader_scan_push_cf_info(context);
+            cf_info->type = VKD3D_SHADER_BLOCK_SWITCH;
+            break;
+        case VKD3DSIH_ENDSWITCH:
+            if (!(cf_info = vkd3d_shader_scan_get_current_cf_info(context))
+                    || cf_info->type != VKD3D_SHADER_BLOCK_SWITCH || cf_info->inside_block)
+            {
+                vkd3d_shader_error(&context->message_context, VKD3D_SHADER_ERROR_TPF_MISMATCHED_CF,
+                        "Encountered ‘endswitch’ instruction without corresponding ‘switch’ block.");
+                return VKD3D_ERROR_INVALID_SHADER;
+            }
+            vkd3d_shader_scan_pop_cf_info(context);
+            break;
+        case VKD3DSIH_CASE:
+            if (!(cf_info = vkd3d_shader_scan_get_current_cf_info(context))
+                    || cf_info->type != VKD3D_SHADER_BLOCK_SWITCH)
+            {
+                vkd3d_shader_error(&context->message_context, VKD3D_SHADER_ERROR_TPF_MISMATCHED_CF,
+                        "Encountered ‘case’ instruction outside switch block.");
+                return VKD3D_ERROR_INVALID_SHADER;
+            }
+            cf_info->inside_block = true;
+            break;
+        case VKD3DSIH_DEFAULT:
+            if (!(cf_info = vkd3d_shader_scan_get_current_cf_info(context))
+                    || cf_info->type != VKD3D_SHADER_BLOCK_SWITCH)
+            {
+                vkd3d_shader_error(&context->message_context, VKD3D_SHADER_ERROR_TPF_MISMATCHED_CF,
+                        "Encountered ‘default’ instruction outside switch block.");
+                return VKD3D_ERROR_INVALID_SHADER;
+            }
+            if (cf_info->has_default)
+            {
+                vkd3d_shader_error(&context->message_context, VKD3D_SHADER_ERROR_TPF_MISMATCHED_CF,
+                        "Encountered duplicate ‘default’ instruction inside the current switch block.");
+                return VKD3D_ERROR_INVALID_SHADER;
+            }
+            cf_info->inside_block = true;
+            cf_info->has_default = true;
+            break;
+        case VKD3DSIH_BREAK:
+            if (!(cf_info = vkd3d_shader_scan_find_innermost_breakable_cf_info(context)))
+            {
+                vkd3d_shader_error(&context->message_context, VKD3D_SHADER_ERROR_TPF_MISMATCHED_CF,
+                        "Encountered ‘break’ instruction outside breakable block.");
+                return VKD3D_ERROR_INVALID_SHADER;
+            }
+            cf_info->inside_block = false;
+            break;
+        case VKD3DSIH_BREAKP:
+            if (!(cf_info = vkd3d_shader_scan_find_innermost_loop_cf_info(context)))
+            {
+                vkd3d_shader_error(&context->message_context, VKD3D_SHADER_ERROR_TPF_MISMATCHED_CF,
+                        "Encountered ‘breakp’ instruction outside loop.");
+                return VKD3D_ERROR_INVALID_SHADER;
+            }
+            break;
+        case VKD3DSIH_CONTINUE:
+            if (!(cf_info = vkd3d_shader_scan_find_innermost_loop_cf_info(context)))
+            {
+                vkd3d_shader_error(&context->message_context, VKD3D_SHADER_ERROR_TPF_MISMATCHED_CF,
+                        "Encountered ‘continue’ instruction outside loop.");
+                return VKD3D_ERROR_INVALID_SHADER;
+            }
+            cf_info->inside_block = false;
+            break;
+        case VKD3DSIH_CONTINUEP:
+            if (!(cf_info = vkd3d_shader_scan_find_innermost_loop_cf_info(context)))
+            {
+                vkd3d_shader_error(&context->message_context, VKD3D_SHADER_ERROR_TPF_MISMATCHED_CF,
+                        "Encountered ‘continue’ instruction outside loop.");
+                return VKD3D_ERROR_INVALID_SHADER;
+            }
+            break;
+        case VKD3DSIH_RET:
+            if (context->cf_info_count)
+                context->cf_info[context->cf_info_count - 1].inside_block = false;
+            break;
         default:
             break;
     }
@@ -581,12 +807,14 @@ static void vkd3d_shader_scan_instruction(struct vkd3d_shader_scan_context *cont
 
     if (vkd3d_shader_instruction_is_uav_counter(instruction))
         vkd3d_shader_scan_record_uav_counter(context, &instruction->src[0].reg);
+
+    return VKD3D_OK;
 }
 
 int vkd3d_shader_scan_dxbc(const struct vkd3d_shader_code *dxbc,
         struct vkd3d_shader_scan_info *scan_info, char **messages)
 {
-    struct vkd3d_shader_message_context message_context;
+    struct vkd3d_shader_message_context *message_context;
     struct vkd3d_shader_instruction instruction;
     struct vkd3d_shader_scan_context context;
     struct vkd3d_shader_parser parser;
@@ -603,24 +831,26 @@ int vkd3d_shader_scan_dxbc(const struct vkd3d_shader_code *dxbc,
         return VKD3D_ERROR_INVALID_ARGUMENT;
     }
 
-    if (!vkd3d_shader_message_context_init(&message_context, VKD3D_SHADER_LOG_INFO, NULL))
+    if (!vkd3d_shader_scan_context_init(&context, scan_info))
         return VKD3D_ERROR;
-    ret = vkd3d_shader_parser_init(&parser, dxbc, &message_context);
-    vkd3d_shader_message_context_trace_messages(&message_context);
-    if (messages && !(*messages = vkd3d_shader_message_context_copy_messages(&message_context)))
-        ret = VKD3D_ERROR_OUT_OF_MEMORY;
-    vkd3d_shader_message_context_cleanup(&message_context);
-    if (ret < 0)
+    message_context = &context.message_context;
+
+    if ((ret = vkd3d_shader_parser_init(&parser, dxbc, message_context)) < 0)
+    {
+        vkd3d_shader_message_context_trace_messages(message_context);
+        if (messages && !(*messages = vkd3d_shader_message_context_copy_messages(message_context)))
+            ret = VKD3D_ERROR_OUT_OF_MEMORY;
+        vkd3d_shader_scan_context_cleanup(&context);
         return ret;
+    }
 
     if (TRACE_ON())
         vkd3d_shader_trace(parser.data);
 
     memset(scan_info, 0, sizeof(*scan_info));
 
-    memset(&context, 0, sizeof(context));
-    context.scan_info = scan_info;
-
+    message_context->line = 2; /* Line 1 is the version token. */
+    message_context->column = 1;
     while (!shader_sm4_is_end(parser.data, &parser.ptr))
     {
         shader_sm4_read_instruction(parser.data, &parser.ptr, &instruction);
@@ -628,18 +858,28 @@ int vkd3d_shader_scan_dxbc(const struct vkd3d_shader_code *dxbc,
         if (instruction.handler_idx == VKD3DSIH_INVALID)
         {
             WARN("Encountered unrecognized or invalid instruction.\n");
-            vkd3d_free(context.uav_ranges);
             vkd3d_shader_free_scan_info(scan_info);
-            vkd3d_shader_parser_destroy(&parser);
-            return VKD3D_ERROR_INVALID_ARGUMENT;
+            ret = VKD3D_ERROR_INVALID_SHADER;
+            goto done;
         }
 
-        vkd3d_shader_scan_instruction(&context, &instruction);
+        if ((ret = vkd3d_shader_scan_instruction(&context, &instruction)) < 0)
+        {
+            vkd3d_shader_free_scan_info(scan_info);
+            goto done;
+        }
+        ++message_context->line;
     }
 
-    vkd3d_free(context.uav_ranges);
+    ret = VKD3D_OK;
+
+done:
+    vkd3d_shader_message_context_trace_messages(message_context);
+    if (messages && !(*messages = vkd3d_shader_message_context_copy_messages(message_context)))
+        ret = VKD3D_ERROR_OUT_OF_MEMORY;
+    vkd3d_shader_scan_context_cleanup(&context);
     vkd3d_shader_parser_destroy(&parser);
-    return VKD3D_OK;
+    return ret;
 }
 
 void vkd3d_shader_free_scan_info(struct vkd3d_shader_scan_info *scan_info)
diff --git a/libs/vkd3d-shader/vkd3d_shader_private.h b/libs/vkd3d-shader/vkd3d_shader_private.h
index c6796bb..4fa17d6 100644
--- a/libs/vkd3d-shader/vkd3d_shader_private.h
+++ b/libs/vkd3d-shader/vkd3d_shader_private.h
@@ -67,6 +67,7 @@ enum vkd3d_shader_error
     VKD3D_SHADER_ERROR_DXBC_INVALID_VERSION      = 4,
     VKD3D_SHADER_ERROR_DXBC_INVALID_CHUNK_OFFSET = 5,
     VKD3D_SHADER_ERROR_DXBC_INVALID_CHUNK_SIZE   = 6,
+    VKD3D_SHADER_ERROR_TPF_MISMATCHED_CF         = 1000,
 };
 
 enum VKD3D_SHADER_INSTRUCTION_HANDLER
-- 
2.11.0




More information about the wine-devel mailing list