Explicitly handle NULL Vulkan debug message IDs

The Vulkan spec states that the pMessageIdName for a debug message may
be NULL. We were previously passing it into a LogMessage (which wraps an
std::ostream) unchecked, but passing a NULL into an ostream is undefined
behavior. This was likely leading to crashes on some devices.

This patch updates the logging behavior to check for NULL and take a
different path if encountered to avoid triggering undefined behavior.

Bug: chromium:1497136
Change-Id: I431539dc21f51fa48bb754f5bc9c85acf2ce8f04
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/159040
Reviewed-by: Austin Eng <enga@chromium.org>
Auto-Submit: Brandon Jones <bajones@chromium.org>
Commit-Queue: Austin Eng <enga@chromium.org>
Kokoro: Kokoro <noreply+kokoro@google.com>
(cherry picked from commit 3a8735eb1fff33fe303b099623c870facb9f7268)
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/159032
Commit-Queue: Brandon Jones <bajones@chromium.org>
Kokoro: Brandon Jones <bajones@chromium.org>
diff --git a/src/dawn/native/vulkan/BackendVk.cpp b/src/dawn/native/vulkan/BackendVk.cpp
index 419c29a..6aba430 100644
--- a/src/dawn/native/vulkan/BackendVk.cpp
+++ b/src/dawn/native/vulkan/BackendVk.cpp
@@ -161,9 +161,10 @@
 
 // Suppress validation errors that are known. Returns false in that case.
 bool ShouldReportDebugMessage(const char* messageId, const char* message) {
-    // pMessageIdName may be NULL
-    if (messageId == nullptr) {
-        return true;
+    // If a driver gives us a NULL pMessage (which would be a violation of the Vulkan spec)
+    // then ignore this message.
+    if (message == nullptr) {
+        return false;
     }
 
     // Some Vulkan drivers send "error" messages of "VK_SUCCESS" when zero devices are
@@ -175,6 +176,12 @@
         return false;
     }
 
+    // The Vulkan spec does allow pMessageIdName to be NULL, but it may still contain a valid
+    // message. Since we can't compare it with our skipped message list allow it through.
+    if (messageId == nullptr) {
+        return true;
+    }
+
     for (const SkippedMessage& msg : kSkippedMessages) {
         if (strstr(messageId, msg.messageId) != nullptr &&
             strstr(message, msg.messageContents) != nullptr) {
@@ -184,6 +191,21 @@
     return true;
 }
 
+void LogCallbackData(LogSeverity severity,
+                     const VkDebugUtilsMessengerCallbackDataEXT* pCallbackData) {
+    LogMessage log = LogMessage(severity);
+
+    // pMessageIdName may be NULL, according to the Vulkan spec. Passing NULL into an ostream is
+    // undefined behavior, so we'll handle that scenario separately.
+    if (pCallbackData->pMessageIdName != nullptr) {
+        log << pCallbackData->pMessageIdName;
+    } else {
+        log << "nullptr";
+    }
+
+    log << ": " << pCallbackData->pMessage;
+}
+
 VKAPI_ATTR VkBool32 VKAPI_CALL
 OnDebugUtilsCallback(VkDebugUtilsMessageSeverityFlagBitsEXT messageSeverity,
                      VkDebugUtilsMessageTypeFlagsEXT /* messageTypes */,
@@ -194,7 +216,7 @@
     }
 
     if (!(messageSeverity & VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT)) {
-        dawn::WarningLog() << pCallbackData->pMessageIdName << ": " << pCallbackData->pMessage;
+        LogCallbackData(LogSeverity::Warning, pCallbackData);
         return VK_FALSE;
     }
 
@@ -220,7 +242,7 @@
 
     // We get to this line if no device was associated with the message. Crash so that the failure
     // is loud and makes tests fail in Debug.
-    dawn::ErrorLog() << pCallbackData->pMessageIdName << ": " << pCallbackData->pMessage;
+    LogCallbackData(LogSeverity::Error, pCallbackData);
     DAWN_ASSERT(false);
 
     return VK_FALSE;