diff --git a/src/libANGLE/renderer/vulkan/TextureVk.cpp b/src/libANGLE/renderer/vulkan/TextureVk.cpp index 3de8b87798..a42b8cf1dc 100644 --- a/src/libANGLE/renderer/vulkan/TextureVk.cpp +++ b/src/libANGLE/renderer/vulkan/TextureVk.cpp @@ -177,6 +177,12 @@ bool CanCopyWithDraw(vk::Renderer *renderer, const angle::FormatID dstFormatID, VkImageTiling destTilingMode) { + // POC PRECONDITION SIMULATION: simulate a source image that lacks + // VK_IMAGE_USAGE_SAMPLED_BIT (e.g. an Android AHardwareBuffer allocated with + // AHARDWAREBUFFER_USAGE_GPU_COLOR_OUTPUT only). This forces the CPU readback + // path (stageSubresourceUpdateFromFramebuffer). On Linux there is no way to + // construct such an image via the GLES2 command buffer, so we hard-code it. + srcUsage &= ~VK_IMAGE_USAGE_SAMPLED_BIT; // Checks that the formats in copy by drawing have the appropriate feature bits bool srcFormatHasNecessaryFeature = (srcUsage & VK_IMAGE_USAGE_SAMPLED_BIT) != 0; bool dstFormatHasNecessaryFeature = vk::FormatHasNecessaryFeature( diff --git a/src/tests/gl_tests/PalettedTextureTest.cpp b/src/tests/gl_tests/PalettedTextureTest.cpp index 991d212fe2..435f7b7e56 100644 --- a/src/tests/gl_tests/PalettedTextureTest.cpp +++ b/src/tests/gl_tests/PalettedTextureTest.cpp @@ -209,6 +209,51 @@ TEST_P(PalettedTextureTestES2, PalettedTextureDraw) EXPECT_PIXEL_COLOR_EQ(31, 31, GLColor::red); } +// POC: glCopyTexSubImage2D from an RGBA8 framebuffer into a GL_PALETTE8_RGBA8_OES +// texture reaches stageSubresourceUpdateFromFramebuffer when CanCopyWithDraw is +// false (e.g. source image lacks VK_IMAGE_USAGE_SAMPLED_BIT). The scratch buffer +// is sized at storageFormat.pixelBytes (=4) * W * H, but PackPixels writes with a +// per-pixel x-stride of destFormat->pixelBytes (=5 for PALETTE8_R8G8B8A8_UNORM), +// overrunning the heap allocation by ~W bytes. +TEST_P(PalettedTextureTestES2, CopyTexSubImagePalettedHeapOOB) +{ + ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("GL_OES_compressed_paletted_texture")); + + constexpr GLsizei W = 256; + constexpr GLsizei H = 256; + + // Source: RGBA8 framebuffer. + GLTexture srcTex; + glBindTexture(GL_TEXTURE_2D, srcTex); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, W, H, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + + GLFramebuffer fbo; + glBindFramebuffer(GL_FRAMEBUFFER, fbo); + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, srcTex, 0); + ASSERT_GL_FRAMEBUFFER_COMPLETE(GL_FRAMEBUFFER); + ASSERT_GL_NO_ERROR(); + + glClearColor(1.0f, 0.0f, 0.0f, 1.0f); + glClear(GL_COLOR_BUFFER_BIT); + + // Destination: GL_PALETTE8_RGBA8_OES texture. + GLTexture dstTex; + glBindTexture(GL_TEXTURE_2D, dstTex); + std::vector palData(256 * 4 + W * H, 0); + glCompressedTexImage2D(GL_TEXTURE_2D, 0, GL_PALETTE8_RGBA8_OES, W, H, 0, + static_cast(palData.size()), palData.data()); + ASSERT_GL_NO_ERROR(); + + // Trigger: copy framebuffer -> paletted texture. Paletted formats are not + // marked `compressed`, so frontend validation accepts this. With the + // CanCopyWithDraw precondition simulated false, this hits + // stageSubresourceUpdateFromFramebuffer and ASAN reports heap-buffer-overflow. + glCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 0, 0, W, H); + glFinish(); +} + // glTexStorage2D with paletted formats should fail. TEST_P(PalettedTextureTestES3, TexStorage2DShouldFail) {