[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