From c9fc70491ed7daf18366927b694c48dd72b4e7a0 Mon Sep 17 00:00:00 2001
From: Mark Lobodzinski <mark@lunarg.com>
Date: Mon, 27 Mar 2017 11:52:02 -0600
Subject: [PATCH] layers: Add pNext cycle/redundancy checks to PV

Change-Id: I06d311821ef0c10683ad8bdaf076231143cde22f
---
 layers/parameter_validation_utils.h | 31 ++++++++++++++++++++++++++---
 1 file changed, 28 insertions(+), 3 deletions(-)

diff --git a/layers/parameter_validation_utils.h b/layers/parameter_validation_utils.h
index 842268f0f..dd67a72e3 100644
--- a/layers/parameter_validation_utils.h
+++ b/layers/parameter_validation_utils.h
@@ -491,12 +491,17 @@ static bool validate_struct_pnext(debug_report_data *report_data, const char *ap
                                   const char *allowed_struct_names, const void *next, size_t allowed_type_count,
                                   const VkStructureType *allowed_types, uint32_t header_version) {
     bool skip_call = false;
+    std::unordered_set<const void *> cycle_check;
+    std::unordered_set<VkStructureType, std::hash<int>> unique_stype_check;
+
     const char disclaimer[] =
         "This warning is based on the Valid Usage documentation for version %d of the Vulkan header.  It "
         "is possible that you are using a struct from a private extension or an extension that was added "
         "to a later version of the Vulkan header, in which case your use of %s is perfectly valid but "
         "is not guaranteed to work correctly with validation enabled";
 
+    // TODO: The valid pNext structure types are not recursive. Each structure has its own list of valid sTypes for pNext.
+    // Codegen a map of vectors containing the allowable pNext types for each struct and use that here -- also simplifies parms.
     if (next != NULL) {
         if (allowed_type_count == 0) {
             std::string message = "%s: value of %s must be NULL.  ";
@@ -509,10 +514,31 @@ static bool validate_struct_pnext(debug_report_data *report_data, const char *ap
             const VkStructureType *end = allowed_types + allowed_type_count;
             const GenericHeader *current = reinterpret_cast<const GenericHeader *>(next);
 
+            cycle_check.insert(next);
+
+
             while (current != NULL) {
-                if (std::find(start, end, current->sType) == end) {
-                    std::string type_name = string_VkStructureType(current->sType);
+                if (cycle_check.find(current->pNext) != cycle_check.end()) {
+                    std::string message = "%s: %s chain contains a cycle -- pNext pointer " PRIx64 " is repeated.";
+                    skip_call |= log_msg(report_data, VK_DEBUG_REPORT_ERROR_BIT_EXT, VK_DEBUG_REPORT_OBJECT_TYPE_UNKNOWN_EXT, 0,
+                                         __LINE__, INVALID_STRUCT_PNEXT, LayerName, message.c_str(), api_name,
+                                         parameter_name.get_name().c_str(), reinterpret_cast<uint64_t>(next));
+                    break;
+                } else {
+                    cycle_check.insert(current->pNext);
+                }
 
+                std::string type_name = string_VkStructureType(current->sType);
+                if (unique_stype_check.find(current->sType) != unique_stype_check.end()) {
+                    std::string message = "%s: %s chain contains duplicate structure types: %s appears multiple times.";
+                    skip_call |= log_msg(report_data, VK_DEBUG_REPORT_ERROR_BIT_EXT, VK_DEBUG_REPORT_OBJECT_TYPE_UNKNOWN_EXT, 0,
+                                         __LINE__, INVALID_STRUCT_PNEXT, LayerName, message.c_str(), api_name,
+                                         parameter_name.get_name().c_str(), type_name.c_str());
+                } else {
+                    unique_stype_check.insert(current->sType);
+                }
+
+                if (std::find(start, end, current->sType) == end) {
                     if (type_name == UnsupportedStructureTypeString) {
                         std::string message =
                             "%s: %s chain includes a structure with unexpected VkStructureType (%d); Allowed "
@@ -532,7 +558,6 @@ static bool validate_struct_pnext(debug_report_data *report_data, const char *ap
                                              header_version, parameter_name.get_name().c_str());
                     }
                 }
-
                 current = reinterpret_cast<const GenericHeader *>(current->pNext);
             }
         }
-- 
GitLab