From 0ee8a060e2b5e6e080abc207e1f1034a1fe998db Mon Sep 17 00:00:00 2001 From: Swung0x48 Date: Sat, 29 Nov 2025 23:29:20 +0800 Subject: [PATCH] [Feat] (MG_Test/Program): add test for malicious shader using `sampler` as variable name --- MobileGL/MG_Test/Program/ProgramTest.cpp | 217 +++++++++++++++++++++++ 1 file changed, 217 insertions(+) diff --git a/MobileGL/MG_Test/Program/ProgramTest.cpp b/MobileGL/MG_Test/Program/ProgramTest.cpp index bc8e7a44..2b53a570 100644 --- a/MobileGL/MG_Test/Program/ProgramTest.cpp +++ b/MobileGL/MG_Test/Program/ProgramTest.cpp @@ -997,3 +997,220 @@ TEST_F(ProgramTest, CompileAndLinkWithExplicitFragmentOut) { ASSERT_TRUE(pSrcfragOut != nullptr) << "Not found expected string in generated shader.\n(Searching for \"" << needle << "\")"; } + +const char* vs_sampler_as_varname = R"(#version 330 +in vec3 Position; +out float sphericalVertexDistance; +out float cylindricalVertexDistance; +out vec4 vertexColor; +out vec2 texCoord0; +void main() { + gl_Position = vec4(Position, 1.0); + sphericalVertexDistance = 1.0; + cylindricalVertexDistance = 1.0; + vertexColor = vec4(Position, 1.0); + texCoord0 = Position.xy; +} +)"; + +const char* fs_sampler_as_varname = R"(#version 330 + +layout(std140) uniform Fog { + vec4 FogColor; + float FogEnvironmentalStart; + float FogEnvironmentalEnd; + float FogRenderDistanceStart; + float FogRenderDistanceEnd; + float FogSkyEnd; + float FogCloudsEnd; +}; + +float linear_fog_value(float vertexDistance, float fogStart, float fogEnd) { + if (vertexDistance <= fogStart) { + return 0.0; + } else if (vertexDistance >= fogEnd) { + return 1.0; + } + + return (vertexDistance - fogStart) / (fogEnd - fogStart); +} + +float total_fog_value(float sphericalVertexDistance, float cylindricalVertexDistance, float environmentalStart, float environmantalEnd, float renderDistanceStart, float renderDistanceEnd) { + return max(linear_fog_value(sphericalVertexDistance, environmentalStart, environmantalEnd), linear_fog_value(cylindricalVertexDistance, renderDistanceStart, renderDistanceEnd)); +} + +vec4 apply_fog(vec4 inColor, float sphericalVertexDistance, float cylindricalVertexDistance, float environmentalStart, float environmantalEnd, float renderDistanceStart, float renderDistanceEnd, vec4 fogColor) { + float fogValue = total_fog_value(sphericalVertexDistance, cylindricalVertexDistance, environmentalStart, environmantalEnd, renderDistanceStart, renderDistanceEnd); + return vec4(mix(inColor.rgb, fogColor.rgb, fogValue * fogColor.a), inColor.a); +} + +float fog_spherical_distance(vec3 pos) { + return length(pos); +} + +float fog_cylindrical_distance(vec3 pos) { + float distXZ = length(pos.xz); + float distY = abs(pos.y); + return max(distXZ, distY); +} + + +layout(std140) uniform Globals { + ivec3 CameraBlockPos; + vec3 CameraOffset; + vec2 ScreenSize; + float GlintAlpha; + float GameTime; + int MenuBlurRadius; + int UseRgss; +}; + + +layout(std140) uniform ChunkSection { + mat4 ModelViewMat; + float ChunkVisibility; + ivec2 TextureSize; + ivec3 ChunkPosition; +}; + +uniform sampler2D Sampler0; + +in float sphericalVertexDistance; +in float cylindricalVertexDistance; +in vec4 vertexColor; +in vec2 texCoord0; + +out vec4 fragColor; + +vec4 sampleNearest(sampler2D sampler, vec2 uv, vec2 pixelSize, vec2 du, vec2 dv, vec2 texelScreenSize) { + // Convert our UV back up to texel coordinates and find out how far over we are from the center of each pixel + vec2 uvTexelCoords = uv / pixelSize; + vec2 texelCenter = round(uvTexelCoords) - 0.5f; + vec2 texelOffset = uvTexelCoords - texelCenter; + + // Move our offset closer to the texel center based on texel size on screen + texelOffset = (texelOffset - 0.5f) * pixelSize / texelScreenSize + 0.5f; + texelOffset = clamp(texelOffset, 0.0f, 1.0f); + + uv = (texelCenter + texelOffset) * pixelSize; + return textureGrad(sampler, uv, du, dv); +} + +vec4 sampleNearest(sampler2D source, vec2 uv, vec2 pixelSize) { + vec2 du = dFdx(uv); + vec2 dv = dFdy(uv); + vec2 texelScreenSize = sqrt(du * du + dv * dv); + return sampleNearest(source, uv, pixelSize, du, dv, texelScreenSize); +} + +// Rotated Grid Super-Sampling +vec4 sampleRGSS(sampler2D source, vec2 uv, vec2 pixelSize) { + vec2 du = dFdx(uv); + vec2 dv = dFdy(uv); + + vec2 texelScreenSize = sqrt(du * du + dv * dv); + float maxTexelSize = max(texelScreenSize.x, texelScreenSize.y); + + float minPixelSize = min(pixelSize.x, pixelSize.y); + + float transitionStart = minPixelSize * 1.0; + float transitionEnd = minPixelSize * 2.0; + float blendFactor = smoothstep(transitionStart, transitionEnd, maxTexelSize); + + float duLength = length(du); + float dvLength = length(dv); + float minDerivative = min(duLength, dvLength); + float maxDerivative = max(duLength, dvLength); + + float effectiveDerivative = sqrt(minDerivative * maxDerivative); + + float mipLevelExact = max(0.0, log2(effectiveDerivative / minPixelSize)); + + float mipLevelLow = floor(mipLevelExact); + float mipLevelHigh = mipLevelLow + 1.0; + float mipBlend = fract(mipLevelExact); + + const vec2 offsets[4] = vec2[]( + vec2(0.125, 0.375), + vec2(-0.125, -0.375), + vec2(0.375, -0.125), + vec2(-0.375, 0.125) + ); + + vec4 rgssColorLow = vec4(0.0); + vec4 rgssColorHigh = vec4(0.0); + for (int i = 0; i < 4; ++i) { + vec2 sampleUV = uv + offsets[i] * pixelSize; + rgssColorLow += textureLod(source, sampleUV, mipLevelLow); + rgssColorHigh += textureLod(source, sampleUV, mipLevelHigh); + } + rgssColorLow *= 0.25; + rgssColorHigh *= 0.25; + + vec4 rgssColor = mix(rgssColorLow, rgssColorHigh, mipBlend); + + vec4 nearestColor = sampleNearest(source, uv, pixelSize, du, dv, texelScreenSize); + + return mix(nearestColor, rgssColor, blendFactor); +} + +void main() { + vec4 color = (UseRgss == 1 ? sampleRGSS(Sampler0, texCoord0, 1.0f / TextureSize) : sampleNearest(Sampler0, texCoord0, 1.0f / TextureSize)) * vertexColor; + color = mix(FogColor * vec4(1, 1, 1, color.a), color, ChunkVisibility); +#ifdef ALPHA_CUTOUT + if (color.a < ALPHA_CUTOUT) { + discard; + } +#endif + fragColor = apply_fog(color, sphericalVertexDistance, cylindricalVertexDistance, FogEnvironmentalStart, FogEnvironmentalEnd, FogRenderDistanceStart, FogRenderDistanceEnd, FogColor); +})"; + +TEST_F(ProgramTest, CompileShaderWithSamplerAsVarName) { + char infoLog[1024] = ""; + + GLuint fs = CreateShader(GL_FRAGMENT_SHADER); + ShaderSource(fs, 1, &fs_sampler_as_varname, NULL); + CompileShader(fs); + GLint fsStatus = GL_FALSE; + GetShaderiv(fs, GL_COMPILE_STATUS, &fsStatus); + GetShaderInfoLog(fs, 1024, nullptr, infoLog); + ASSERT_EQ(fsStatus, GL_TRUE) << infoLog; + + GLuint vs = CreateShader(GL_VERTEX_SHADER); + ShaderSource(vs, 1, &vs_sampler_as_varname, NULL); + CompileShader(vs); + GLint vsStatus = GL_FALSE; + GetShaderiv(vs, GL_COMPILE_STATUS, &vsStatus); + GetShaderInfoLog(vs, 1024, nullptr, infoLog); + ASSERT_EQ(vsStatus, GL_TRUE) << infoLog; + + GLuint program = CreateProgram(); + AttachShader(program, fs); + AttachShader(program, vs); + + LinkProgram(program); + GLint linkStatus = GL_FALSE; + GetProgramiv(program, GL_LINK_STATUS, &linkStatus); + ASSERT_EQ(linkStatus, GL_TRUE); + printf("Program linked.\n"); + + UseProgram(program); + + auto programObject = MG_State::pGLContext->GetCurrentProgram(); + auto& spirvs = programObject->GetGeneratedSpirv(); + auto& fragSpirv = spirvs[0]; // 0 - fragment, 1 - vertex + char* pSrcfragOut = nullptr; + MG_Util::ShaderTranspiler::SpvcSession spvcSession(fragSpirv); + spvc_compiler_options options; + spvcSession.CreateOptions(&options); + + spvc_compiler_options_set_uint(options, SPVC_COMPILER_OPTION_GLSL_VERSION, 460); + spvc_compiler_options_set_bool(options, SPVC_COMPILER_OPTION_GLSL_ES, SPVC_TRUE); + // spvc_compiler_options_set_bool(options, SPVC_COMPILER_OPTION_GLSL_VULKAN_SEMANTICS, SPVC_FALSE); + + spvcSession.SetOptions(options); + + const char* result = nullptr; + spvcSession.Compile(&result); + printf("%s\n\n", result); +}