libvulkan: Fix dEQP-VK.api.object_management.alloc_callback_fail.instance
The loader was crashing when a std::vector::resize() operation called
the test-provided allocator, which returned failure, and then the
vector blindly started writing into the returned pointer.
Obvious in hindsight, but stdlib containers+strings + user-provided
allocation funcs implies that the loader must be built with exceptions
enabled, and must be exception-safe at least where it uses
containers/strings. We were doing neither.
This change has the minimally invasive fix, which is to (a) throw an
exception from the stdlib Allocator when the app-provided allocation
function fails, and (b) wrap every stdlib operation that might
allocate in a try..catch and turn it into a
VK_ERROR_OUT_OF_HOST_MEMORY error.
This is pretty unsatisfying and I'm not happy with the resulting
mismash of error-handling styles, with having exceptions at all in
code that was not written to be exception-safe, or with the
fine-grained try..catch. We need to decide whether to keep using parts
of stdlib that can allocate, and rewrite a lot of code to be
exception-friendly, or we need to replace the stdlib code with manual
containers and strings. Bug 26732452 filed.
Change-Id: I6f096f25a43a0e3c5f56796c2af19f114d2edac6
(cherry picked from commit ccca46db073dfadc81a68ac1533d8859ed3e109a)
diff --git a/vulkan/libvulkan/swapchain.cpp b/vulkan/libvulkan/swapchain.cpp
index 75cabc1..2392b5c 100644
--- a/vulkan/libvulkan/swapchain.cpp
+++ b/vulkan/libvulkan/swapchain.cpp
@@ -77,10 +77,13 @@
: allocator_(other.allocator_), scope_(other.scope_) {}
T* allocate(size_t n) const {
- return static_cast<T*>(allocator_.pfnAllocation(
+ T* p = static_cast<T*>(allocator_.pfnAllocation(
allocator_.pUserData, n * sizeof(T), alignof(T), scope_));
+ if (!p)
+ throw std::bad_alloc();
+ return p;
}
- void deallocate(T* p, size_t) const {
+ void deallocate(T* p, size_t) const noexcept {
return allocator_.pfnFree(allocator_.pUserData, p);
}
@@ -93,10 +96,15 @@
template <typename T, typename Host>
std::shared_ptr<T> InitSharedPtr(Host host, T* obj) {
- obj->common.incRef(&obj->common);
- return std::shared_ptr<T>(
- obj, NativeBaseDeleter<T>(),
- VulkanAllocator<T>(*GetAllocator(host), AllocScope<Host>::kScope));
+ try {
+ obj->common.incRef(&obj->common);
+ return std::shared_ptr<T>(
+ obj, NativeBaseDeleter<T>(),
+ VulkanAllocator<T>(*GetAllocator(host), AllocScope<Host>::kScope));
+ } catch (std::bad_alloc&) {
+ obj->common.decRef(&obj->common);
+ return nullptr;
+ }
}
// ----------------------------------------------------------------------------
@@ -161,6 +169,12 @@
Surface* surface = new (mem) Surface;
surface->window = InitSharedPtr(instance, pCreateInfo->window);
+ if (!surface->window) {
+ ALOGE("surface creation failed: out of memory");
+ surface->~Surface();
+ allocator->pfnFree(allocator->pUserData, surface);
+ return VK_ERROR_OUT_OF_HOST_MEMORY;
+ }
// TODO(jessehall): Create and use NATIVE_WINDOW_API_VULKAN.
int err =
@@ -468,6 +482,13 @@
break;
}
img.buffer = InitSharedPtr(device, buffer);
+ if (!img.buffer) {
+ ALOGE("swapchain creation failed: out of memory");
+ surface.window->cancelBuffer(surface.window.get(), buffer,
+ img.dequeue_fence);
+ result = VK_ERROR_OUT_OF_HOST_MEMORY;
+ break;
+ }
img.dequeued = true;
image_create.extent =