summaryrefslogtreecommitdiff
path: root/Source/Core/VideoBackends/Vulkan/ObjectCache.cpp
diff options
context:
space:
mode:
authorStenzek <stenzek@gmail.com>2016-11-13 18:50:10 +1000
committerStenzek <stenzek@gmail.com>2016-11-28 21:21:55 +1000
commit8d48319414d7e3f348e4ef38e976121e9a1fce6a (patch)
tree3875647817c8ef5e0f8843f02bba13fc1f12e91d /Source/Core/VideoBackends/Vulkan/ObjectCache.cpp
parent9604b336c8213c43dff90fab67df8eaa31cdbbb5 (diff)
Vulkan: Validate the pipeline cache before using it
This ensures that if a user changes adapters or vendors we're not passing invalid data to the driver.
Diffstat (limited to 'Source/Core/VideoBackends/Vulkan/ObjectCache.cpp')
-rw-r--r--Source/Core/VideoBackends/Vulkan/ObjectCache.cpp77
1 files changed, 77 insertions, 0 deletions
diff --git a/Source/Core/VideoBackends/Vulkan/ObjectCache.cpp b/Source/Core/VideoBackends/Vulkan/ObjectCache.cpp
index d7d505b793..761402e478 100644
--- a/Source/Core/VideoBackends/Vulkan/ObjectCache.cpp
+++ b/Source/Core/VideoBackends/Vulkan/ObjectCache.cpp
@@ -344,6 +344,13 @@ bool ObjectCache::CreatePipelineCache(bool load_from_disk)
disk_data.clear();
}
+ if (!disk_data.empty() && !ValidatePipelineCache(disk_data.data(), disk_data.size()))
+ {
+ // Don't use this data. In fact, we should delete it to prevent it from being used next time.
+ File::Delete(m_pipeline_cache_filename);
+ disk_data.clear();
+ }
+
VkPipelineCacheCreateInfo info = {
VK_STRUCTURE_TYPE_PIPELINE_CACHE_CREATE_INFO, // VkStructureType sType
nullptr, // const void* pNext
@@ -369,6 +376,76 @@ bool ObjectCache::CreatePipelineCache(bool load_from_disk)
return false;
}
+// Based on Vulkan 1.0 specification,
+// Table 9.1. Layout for pipeline cache header version VK_PIPELINE_CACHE_HEADER_VERSION_ONE
+// NOTE: This data is assumed to be in little-endian format.
+#pragma pack(push, 4)
+struct VK_PIPELINE_CACHE_HEADER
+{
+ u32 header_length;
+ u32 header_version;
+ u32 vendor_id;
+ u32 device_id;
+ u8 uuid[VK_UUID_SIZE];
+};
+#pragma pack(pop)
+// TODO: Remove the #if here when GCC 5 is a minimum build requirement.
+#if defined(__GNUC__) && !defined(__clang__) && __GNUC__ < 5
+static_assert(std::has_trivial_copy_constructor<VK_PIPELINE_CACHE_HEADER>::value,
+ "VK_PIPELINE_CACHE_HEADER must be trivially copyable");
+#else
+static_assert(std::is_trivially_copyable<VK_PIPELINE_CACHE_HEADER>::value,
+ "VK_PIPELINE_CACHE_HEADER must be trivially copyable");
+#endif
+
+bool ObjectCache::ValidatePipelineCache(const u8* data, size_t data_length)
+{
+ if (data_length < sizeof(VK_PIPELINE_CACHE_HEADER))
+ {
+ ERROR_LOG(VIDEO, "Pipeline cache failed validation: Invalid header");
+ return false;
+ }
+
+ VK_PIPELINE_CACHE_HEADER header;
+ std::memcpy(&header, data, sizeof(header));
+ if (header.header_length < sizeof(VK_PIPELINE_CACHE_HEADER))
+ {
+ ERROR_LOG(VIDEO, "Pipeline cache failed validation: Invalid header length");
+ return false;
+ }
+
+ if (header.header_version != VK_PIPELINE_CACHE_HEADER_VERSION_ONE)
+ {
+ ERROR_LOG(VIDEO, "Pipeline cache failed validation: Invalid header version");
+ return false;
+ }
+
+ if (header.vendor_id != g_vulkan_context->GetDeviceProperties().vendorID)
+ {
+ ERROR_LOG(VIDEO,
+ "Pipeline cache failed validation: Incorrect vendor ID (file: 0x%X, device: 0x%X)",
+ header.vendor_id, g_vulkan_context->GetDeviceProperties().vendorID);
+ return false;
+ }
+
+ if (header.device_id != g_vulkan_context->GetDeviceProperties().deviceID)
+ {
+ ERROR_LOG(VIDEO,
+ "Pipeline cache failed validation: Incorrect device ID (file: 0x%X, device: 0x%X)",
+ header.device_id, g_vulkan_context->GetDeviceProperties().deviceID);
+ return false;
+ }
+
+ if (std::memcmp(header.uuid, g_vulkan_context->GetDeviceProperties().pipelineCacheUUID,
+ VK_UUID_SIZE) != 0)
+ {
+ ERROR_LOG(VIDEO, "Pipeline cache failed validation: Incorrect UUID");
+ return false;
+ }
+
+ return true;
+}
+
void ObjectCache::DestroyPipelineCache()
{
for (const auto& it : m_pipeline_objects)